All BASIC

Message Boards => Scripting Languages => Topic started by: John on November 26, 2017, 12:56:49 PM

Title: Script BASIC COM
Post by: John on November 26, 2017, 12:56:49 PM
I'n no COM/OLE pro but I have been spending some time with it trying to get the Script BASIC COM extension module working with the Sage 100 accounting software  BOI (Busness Object Interface) I have it working in VBScript but still not 100% there yet with Script BASIC. See the Open Sage Forum (http://opensage.org/forum/index.php) Code Challange for where I'm at with it.

Title: Re: Script BASIC COM
Post by: petelomax on November 28, 2017, 05:23:13 PM
Well, without a working version of Sage there is little I can even try.

Actually, I used to work for online50 (and yes it is Sage 50 not Sage 100) - while I have no interest in promoting their services anymore, if what you need is a business solution, as opposed to a programming solution, you could do worse than peruse http://www.online50.net/Online_Sage/Sage50AccountsAddOnsOnline.html as there are a numer of "Adept" extensions, one of which might just possibly fit the bill. Before you ask, there is no way I could get any source code, but with a bit of google-fu you might find someone worth talking to.
Title: Re: Script BASIC COM
Post by: John on November 29, 2017, 09:06:09 AM
Thanks Pete fpr having a peek at my Sage issue with Script BASIC COM.

The reason I think I'm getting a ProvideX ERR=95 is that the Script BASIC COM extension module isn't passing the VT_Dispatch object (SY_Session) as an object.  Is have no problem using  SY_Session to call methods or get/let properties. I feel I'm close to getting this working after I discovered I needed Script BASIC to be a Windows process and not a console process. (got me by the ERR=65 issue)


Here is the Bitbucket Repository Script BASIC COM (https://bitbucket.org/ScriptBasic/com) if you would be so kind to have a look. The COM.cpp is the extension module interface.

Title: Re: Script BASIC COM
Post by: petelomax on November 30, 2017, 09:18:37 AM
All a bit above my pay grade, unfortunately. The only (minor, very minor) thing I noticed was the free(sz) in __C2W should probably be moved up one line to be inside the if.

Actually, turning this around on its head, can you replicate the fileopen (see links above) in ScriptBasic? The aim is pretty simple: to have a file open/save that has
an ansi/utf8/utf16 encoding dropdown that auto-changes as you select different files and, of course, can be manually overidden - exactly like the standard Windows Notepad.
Title: Re: Script BASIC COM
Post by: John on November 30, 2017, 12:28:40 PM
Thanks Pete for having a look. I can see your point of moving free(sz) into the IF block. I'm not sure if this will solve my problem with the ERR=95 in the ProvideX COM interface but it's worth fixing. I see a lot of GetIDsOfNames failures trying to call methods and maybe this is related.

What looks like that is happening is osession which Script BASIC sees as a VT_DISPATCH type prior to the ProvideX.Script NewObject() call to instantiate the AR_Customer_bus business object isn't seeing the argument as a passed VT_DISPATCH object reference but a VT_LONG. Calling NewObject without the object reference as an argument as in the SY_Session() call works fine. If I can just get by this issue, I feel everything will work and give me full control over the BOI interface and not have to count on static VBScripts to process the requests.  :-\

I recompiled the COM interface with the suggested change and still having the same issue.


C:\ScriptBASIC\examples>sbwin comcustcc.sb
The number of arguments is: 1
CreateObject(ProvideX.Script)
CallByName 4 args
CallByName(obj=4bb454, method='Init', calltype=1 , comArgs=1)
CallByName 4 args
CallByName(obj=4bb454, method='NewObject', calltype=1 , comArgs=1)
return value from COM function was numeric: 4961652
CallByName 5 args
CallByName(obj=4bb574, method='nSetUser', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4bb574, method='nsetcompany', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4bb574, method='nSetDate', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4bb574, method='nSetModule', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4bb574, method='nLookupTask', calltype=1 , comArgs=1)
return value from COM function was numeric: 40000001
CallByName 4 args
CallByName(obj=4bb574, method='nSetProgram', calltype=1 , comArgs=1)
return value from COM function was numeric: 100006
CallByName 5 args
CallByName(obj=4bb454, method='NewObject', calltype=1 , comArgs=2)
Invoke failed
CallByName 4 args
CallByName(NULL) called
CallByName 4 args
CallByName(NULL) called
CallByName 3 args
CallByName(NULL) called
CallByName 5 args
CallByName(NULL) called
CallByName 5 args
CallByName(NULL) called
CallByName 5 args
CallByName(NULL) called
CallByName 5 args
CallByName(NULL) called
CallByName 5 args
CallByName(NULL) called
CallByName 2 args
CallByName(obj=4bb574, method='DropObject', calltype=1 , comArgs=0)

C:\ScriptBASIC\examples>

Title: Re: Script BASIC COM
Post by: John on November 30, 2017, 12:54:22 PM
Quote
Actually, turning this around on its head, can you replicate the fileopen (see links above) in ScriptBasic? The aim is pretty simple: to have a file open/save that has
an ansi/utf8/utf16 encoding dropdown that auto-changes as you select different files and, of course, can be manually overidden - exactly like the standard Windows Notepad.

Charles Pegge (OxygenBasic author) wrote a FFI on steroids (DLLC) as an extension module for Script BASIC. It handles wide strings, BStrings, conversions and auto freeing.
Title: Re: Script BASIC COM
Post by: John on November 30, 2017, 07:18:27 PM
This looks suspcious.

besFUNCTION(CallByName)

Code: C
  1.         case VT_DISPATCH:
  2.  
  3.                 //if(retVal.vt == VT_DISPATCH) todo: register handle
  4.                 if(com_dbg) color_printf(colors::myellow,"return value from COM function was numeric: %d\n", retVal.lVal);
  5.         LONGVALUE(besRETURNVALUE) = retVal.lVal;
  6.                 break;
  7.  

register handle - Seems like a good idea. It looks like I was right about passing the LONG object pointer reference.

Can you provide any help with registering the VT_DISPATCH handle? (if you agree this may be the problem)

FYI: An ERR=95 means the object doesn't exists. Makes sense if it's not registered.
Title: Re: Script BASIC COM
Post by: John on December 02, 2017, 06:03:08 PM
The DescribeInterface function provides a basic typelib viwewer. What caught my eye is the Script BASIC argument being a previously defined object pointer required
Code: C
  1. IDispatch* IDisp = (IDispatch*)LONGVALUE(Argument);
  2.  
before calling the typelib viewer. I'm wondering if this same assignment used for the passed object handle that currently is passed aLONG as an object pointer reference,  Basically giving the argument an iDispatch reference.

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.  
Title: Re: Script BASIC COM
Post by: John on December 04, 2017, 06:36:10 PM
I don't believe the argument assignment for loop ever addresses a type of VT_DISPATCH. The argument is being passed as a type VT_DISPATCH but I fear the argument never gets assigned for the INVOKE if it was passed as a VT_DISPATCH type.

Code: C
  1.   /* map in argument values and types    ->[ IN REVERSE ORDER ]<-    */
  2.   for(int i=0; i < com_args; i++){
  3.           VARIABLE arg_x;              
  4.           arg_x = besARGUMENT(3 + com_args - i);
  5.           besDEREFERENCE(arg_x);
  6.  
  7.                 switch( TYPE(arg_x) ){ //script basic type to COM variant type
  8.  
  9.                           case VTYPE_DOUBLE:
  10.                           case VTYPE_ARRAY:
  11.                           case VTYPE_REF:
  12.                                 RETURN0("Arguments of script basic types [double, ref, array] not supported")
  13.                                 break;
  14.  
  15.                           case VTYPE_LONG:
  16.                                 pvarg[i].vt = VT_I4;
  17.                                 pvarg[i].lVal = LONGVALUE(arg_x);
  18.                                 break;
  19.                          
  20.                           case VTYPE_STRING:
  21.                                 char* myStr = GetCString(arg_x);
  22.                                
  23.                                 //peek at data and see if an explicit VT_ type was specified.. scriptbasic only supports a few types
  24.                                 if( !HandleSpecial(&pvarg[i], myStr) ){
  25.                                         //nope its just a standard string type
  26.                                         LPWSTR wStr = __C2W(myStr);
  27.                                         BSTR bstr = SysAllocString(wStr);
  28.                                         bstrs.push_back(bstr); //track these to free after call to prevent leak
  29.                                         pvarg[i].vt = VT_BSTR;
  30.                                         pvarg[i].bstrVal = bstr;
  31.                                         free(myStr);
  32.                                         free(wStr);
  33.                                 }
  34.  
  35.                                 break;                   
  36.                                
  37.           }
  38.  
  39.   }
  40.  


I added an additional debug print to display the argument types and values just prior to the INVOKE.

CallByName(obj=3750bc, method='NewObject', calltype=1 , comArgs=2)

pvarg[0].vt=3, pvarg[1].vt=8

CallByName(obj=2850bc, method='NewObject', calltype=1 , comArgs=2)
pvarg[0].lVal=2642396, pvarg[1].lVal=2712612

The question is did the predefined object handle (SY_Session) get passed as a LONG to INVOKE and not as DISPATCH pointer?

Charles Pegge (OxygenBasic author) sent me an informative article about COM in plain C (https://www.codeproject.com/Articles/13601/COM-in-plain-C).
Title: Re: Script BASIC COM
Post by: John on December 06, 2017, 07:30:22 PM
I think I may be able to fix this by adding a special VT_ type for getting the IDispatch pointer before doing the Invoke. Dave already supports a couple VT_ special types for wide and BStr arguments.

If that doesn't do it, then I'm at a loss.
Title: Re: Script BASIC COM
Post by: John on December 07, 2017, 07:05:04 PM
This is going to my attempt to try to handle objects as CallByName arguments enabling and extending Dave's special VT_ types routine. I going to add the VT_DISPATCH to the routine and get the IDispatch handle before doing the CallByName method Invoke.

Code: C
  1.                                 //peek at data and see if an explicit VT_ type was specified.. scriptbasic only supports a few types
  2.                                 if( !HandleSpecial(&pvarg[i], myStr) ){
  3.                                         //nope its just a standard string type
  4.                                         LPWSTR wStr = __C2W(myStr);
  5.                                         BSTR bstr = SysAllocString(wStr);
  6.                                         bstrs.push_back(bstr); //track these to free after call to prevent leak
  7.                                         pvarg[i].vt = VT_BSTR;
  8.                                         pvarg[i].bstrVal = bstr;
  9.                                         free(myStr);
  10.                                         free(wStr);
  11.                                 }
  12.  

Code: C
  1. bool HandleSpecial(VARIANTARG* va, 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.          
  9.         if(s.length() < 3) return false;
  10.         if(s.substr(0,3) != "VT_") return false;
  11.        
  12.         int pos = s.find(":",0);
  13.         if(pos < 1) return false;
  14.  
  15.         std::string cmd = s.substr(0,pos);
  16.         if(s.length() < pos+2) return false;
  17.  
  18.         s = s.substr(pos+1);
  19.  
  20.         //todo implement handling of these types (there are many more than this)
  21.         if(cmd == "VT_I1"){
  22.         }else if(cmd == "VT_I2"){
  23.         }else if(cmd == "VT_I8"){
  24.     }else if(cmd == "VT_BOOL"){
  25.         }
  26.        
  27.         return true;
  28. }
  29.  
Title: Re: Script BASIC COM
Post by: John on December 09, 2017, 07:59:44 PM
I'm almost there but having an issue conversing a string to a IDispatch pointer. Any suggestions would be appreciated.

As part of the CallByName argument checking and assigning for LONG and STRING values, it also checks for special types. Dave disabled the routine think it was needed for this first cut of the interface. I enable to prefix VT_ special type function and tried to assign the argument array for CallByName with a IDispatch pointer to the object pointer (LONG) passed in the call.

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


This is the HandleSpecial routine I enabled and trying to use to assign an IDispatch pointer using the object pointer (SY_Session / osession) being passed as a string value. I'm having a problem with making the va (current com_arg array index) assignment.

Here is the COM.cpp (https://bitbucket.org/ScriptBasic/com/src/19f25e14e98ff7835b6f6560db272734e34c4edb/engine/COM_Extension_DLL/COM.cpp?at=master&fileviewer=file-view-default) original source on the Bitbucket repository.

Code: C
  1. // the idea behind this one is that we can use a string to embed a type specifier
  2. // to explicitly declare and cast a variable to the type we want such as "VT_I2:2"
  3. //
  4. // in testing with VB6 however, if we pass .vt = VT_I4 when vb6 expects a VT_I1 (char)
  5. // it works as long as the value is < 255, also works with VT_BOOL
  6. //
  7. // do we really need this function ? I prefer less complexity if possible.
  8. //
  9. // Note: there are many COM types, I have no plans to cover them all
  10.  
  11. bool HandleSpecial(VARIANTARG* va, char* str){
  12.  
  13.         // return false; //disabled for now see notes above..
  14.  
  15.         if(str==0) return false;
  16.  
  17.         std::string s = str;
  18.          
  19.         if(s.length() < 3) return false;
  20.         if(s.substr(0,3) != "VT_") return false;
  21.        
  22.         int pos = s.find(":",0);
  23.         if(pos < 1) return false;
  24.  
  25.         std::string cmd = s.substr(0,pos);
  26.         if(s.length() < pos+2) return false;
  27.  
  28.         s = s.substr(pos+1);
  29.  
  30.         //todo implement handling of these types (there are many more than this)
  31.         if(cmd == "VT_I1"){
  32.         }else if(cmd == "VT_I2"){
  33.         }else if(cmd == "VT_I8"){
  34.   }else if(cmd == "VT_BOOL"){
  35.   }else if(cmd == "VT_DISPATCH"){
  36.         IDispatch* va = (IDispatch*)LONGVALUE(atoi(s));
  37.         }
  38.        
  39.         return true;
  40. }
  41.  
Title: Re: Script BASIC COM
Post by: erosolmi on December 10, 2017, 03:58:23 AM
Ciao John

va is already a VARIANTARG pointer, you cannot cast to another type like "IDispatch* va = (IDispatch*)"
See: https://msdn.microsoft.com/en-us/library/ms891678.aspx

You just need to assign a value to va.vt = VT_DISPATCH (or VT_DISPATCH | VT_BYREF if passed BYREF) that is the type of value inside va.
And put populate correct va member: put dispatch pointer you have into the string into va.pdispVal (or va.ppdispVal if passed BYREF)

I know what you are doing because I did for thinBasic.
In any case I think ScriptBasic is on the wrong path, I mean ... why to give to user the complexity to use something like CallByName(<object>, ...) syntax?
Try to give the user a syntax like <objectname>.<property> or <objectname>.<method>

In any case passing from a CallByName(<object>, ...) is a good step to understand what's going on.

Ciao and good luck
Title: Re: Script BASIC COM
Post by: John on December 10, 2017, 10:51:59 AM
Thank You Eros for helping out with this problem I've been struggling with for a couple weeks.

Could you be so kind to show me the replacement line of code that would make this work? My knowledge of C++ and low level COM/OLE is marginal at best.

The va variable (passed byref) is actually the CallByName() &pvarg index i I'm tying to populate with a passed string version (converted to a LONG) of the SY_Session object pointer. Currently the Invoke is seeing the pointer as just a LONG which fails.

The argument passed is "VT_DISPATCH:" & osession. SB converts the LONG pointer to a string when using a concatenating & operator.
Title: Re: Script BASIC COM
Post by: erosolmi on December 10, 2017, 12:00:53 PM
John,

have a look at Jose CallByName: https://forum.powerbasic.com/forum/user-to-user-discussions/source-code/24964-com-callbyname
The general idea is that in order to call a method/property of an interface, you need:

Start from very simple, that are numbers.
Then add strings that are BSTR using SYS* functions
Passing a Dispatch object is nothing  more than passing a variant of type VT_DISPATCH and setting the correct pointer.

When done with numbers let me know and I will help more

Ciao
Eros
Title: Re: Script BASIC COM
Post by: John 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.  
Title: Re: Script BASIC COM
Post by: John 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.
Title: Re: Script BASIC COM
Post by: erosolmi 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:

If your VARIANTARG element contains a reference to an IDIspatch interface:

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
 

Title: Re: Script BASIC COM
Post by: Mike Lobanovsky 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/ (http://www.cplusplus.com/reference/string/string/string/)

Hope this helps to kill this immediate error.
Title: Re: Script BASIC COM
Post by: John 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
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 11, 2017, 06:18:48 AM
John,

Exactly what is the code written in lines 507 to 509 of com.cpp, please?
Title: Re: Script BASIC COM
Post by: John 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.


Title: Re: Script BASIC COM
Post by: Mike Lobanovsky 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?
Title: Re: Script BASIC COM
Post by: John 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
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 11, 2017, 06:50:26 AM
Thanks John,

I'll see what I can do later on this (local) evening.
Title: Re: Script BASIC COM
Post by: John on December 11, 2017, 06:54:00 AM
Thanks Mike!!!

I think we are close.
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky 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. }
Title: Re: Script BASIC COM
Post by: John 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>
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky 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.
Title: Re: Script BASIC COM
Post by: John 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?
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 11, 2017, 05:47:43 PM
Can you see why I might be getting the GetIDsOfNames failed message?

Probably yes but not tonight, John. And at any rate I'd need a full install of SB and whatever else you're using for testing the COM module.

Also, the only VS2008 I have is EE which means it lacks the ATL support and some other pro features. I used VS2013 to convert, recompile and check the COM module for errors. It yielded a somewhat larger DLL file though.

Title: Re: Script BASIC COM
Post by: John on December 11, 2017, 06:01:49 PM
I took a peek at the methods that are failing and there shouldn't be any reason they shouldn't work.

Today was a huge wall you got us over. (getting the NewObject method to return a valid pointer passing an object pointer as an argument)


Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 11:13:14 AM
Mike,

I received an e-mail from Josť Roca with a comment about your fix for the HandleSpecial() routine.

Quote
In his posted workaround it is missing to increase the reference count of the dispatch pointer.

I'm not sure if this is causing the GetIDsOfNames failed errors or not.

I have asked Josť Roca to join us here on the forum. I hope he accepts my invitation,


John
Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 11:55:30 AM
Mike,

Based on the documentation for the AR_Customer_bus object, I don't see where the SY_Service object which contains the methods that are currently generating an error. Why the VBScript version works, I haven't a clue. I substituted the AR_Customer_bus for AR_Customer_svc object which does have the SY_Service object inherited and the calls work without error. I'm still not getting my message box to show a customer record yet but at least the COM interface isn't generating errors Invoking the methods.

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. ' HomeDirOK = COM::CallByName(oscript, "Init", vbMethod, NT::RegRead("HKEY_CURRENT_USER\\Software\\ODBC\\ODBC.INI\\SOTAMAS90\\Directory") & "\\HOME")
  8. COM::CallByName(oScript,"Init",vbMethod,"C:\\Sage\\Sage 100 Advanced ERP\\MAS90\\HOME")
  9. osession = COM::CallByName(oscript, "NewObject", vbMethod, "SY_Session")
  10. UserOK = COM::CallByName(osession, "nSetUser", vbMethod, "JRS", "northstar")
  11. CompanyOK = COM::CallByName(osession, "nsetcompany", vbMethod, "ABC")
  12. DateOK = COM::CallByName(osession, "nSetDate", vbMethod, "A/R", "20171119")
  13. ModuleOK = COM::CallByName(osession, "nSetModule", vbMethod, "A/R")
  14. osec = COM::CallByName(osession, "nSetProgram", vbMethod, COM::CallByName(osession, "nLookupTask", vbMethod, "AR_Customer_ui"))
  15. ' ocust = COM::CallByName(oscript, "NewObject", vbMethod, "AR_Customer_ui", "VT_DISPATCH:" & osession)
  16. ocust = COM::CallByName(oscript, "NewObject", vbMethod, "AR_Customer_svc", "VT_DISPATCH:" & osession)
  17. retval = 0
  18. CustomerNo = ""
  19. CustomerName = ""
  20. City = ""
  21. State = ""
  22. TelephoneNo = ""
  23.  
  24. retval = COM::CallByName(ocust, "nSetIndex", vbMethod, "KNAME")
  25. retval = COM::CallByName(ocust, "nFind", vbMethod, "00ABC")
  26. retval = COM::CallByName(ocust, "nMoveNext", vbMethod)
  27. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerNo$", CustomerNo)
  28. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerName$", CustomerName)
  29. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "City$", City)
  30. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "State$", State)
  31. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "TelephoneNo$", TelephoneNo)
  32.  
  33. retval = NT::MsgBox("Customer:  " & CustomerNo & "  " & CustomerName & "  " & City & "  " & State & "  " & TelephoneNo, "PVXCOM Code Challenge", "OK")
  34. COM::CallByName(osession,"DropObject")
  35. COM::ReleaseObject(oscript)
  36.  


C:\ScriptBASIC\examples>sbwin comcustcc.sb
The number of arguments is: 1
CreateObject(ProvideX.Script)
CallByName 4 args
CallByName(obj=4650bc, method='Init', calltype=1 , comArgs=1)
CallByName 4 args
CallByName(obj=4650bc, method='NewObject', calltype=1 , comArgs=1)
return value from COM function was numeric: 4608476
CallByName 5 args
CallByName(obj=4651dc, method='nSetUser', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4651dc, method='nsetcompany', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=4651dc, method='nSetDate', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4651dc, method='nSetModule', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=4651dc, method='nLookupTask', calltype=1 , comArgs=1)
return value from COM function was numeric: 40000001
CallByName 4 args
CallByName(obj=4651dc, method='nSetProgram', calltype=1 , comArgs=1)
return value from COM function was numeric: 100006
CallByName 5 args
CallByName(obj=4650bc, method='NewObject', calltype=1 , comArgs=2)
return value from COM function was numeric: 4608620
CallByName 4 args
CallByName(obj=46526c, method='nSetIndex', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=46526c, method='nFind', calltype=1 , comArgs=1)
return value from COM function was numeric: 0
CallByName 3 args
CallByName(obj=46526c, method='nMoveNext', calltype=1 , comArgs=0)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=46526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=46526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=46526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=46526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=46526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 2 args
CallByName(obj=4651dc, method='DropObject', calltype=1 , comArgs=0)

C:\ScriptBASIC\examples>
Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 12, 2017, 01:19:51 PM
Re: Jose Roca

No problem, John. Compile this and see what happens: (actually it contains a question to Jose)
Code: C
  1. bool HandleSpecial(VARIANTARG* va, char* str) {
  2.  
  3.     if (str == 0) return false;
  4.  
  5.     std::string s = str;
  6.     //std::string s(str);
  7.  
  8.     if (s.length() < 3) return false;
  9.     if (s.substr(0, 3) != "VT_") return false;
  10.  
  11.     int pos = s.find(":", 0);
  12.     if (pos < 1) return false;
  13.  
  14.     std::string cmd = s.substr(0, pos);
  15.     if (s.length() < pos + 2) return false;
  16.  
  17.     s = s.substr(pos + 1);
  18.  
  19.     //todo implement handling of these types (there are many more than this)
  20.     if (cmd == "VT_I1") {
  21.     } else if (cmd == "VT_I2") {
  22.     } else if (cmd == "VT_I8") {
  23.     } else if (cmd == "VT_BOOL") {
  24.     } else if (cmd == "VT_DISPATCH") {
  25.         // HACK: va->lVal and va->pdispVal are a union, so ...
  26.         va->vt = VT_DISPATCH; // ... we tag this variant as an IDispatch* ...
  27.         va->lVal = atol(s.c_str()); // ... but write its ptr value as an ordinary long to avoid compiler type mismatches
  28.         // @Jose Roca: But where do you suggest I put a matching Release() call?
  29.         // I am NOT A CLIENT, I am JUST A PROXY passing the pointer value but not using it in any way.
  30.         // It's ProvideX.Script which is the IDispatch's client that's supposed to manage its references.
  31.         va->pdispVal->AddRef();
  32.     }
  33.  
  34.     return true;
  35. }

Re: _bus vs. _svc

John, you're mostly talking to yourself here (and evidently dropping out words at times because I can't grasp the meaning of very first sentence). I am not yet in the subject of this project, far from it.

WHICH METHOD CALLS EXACTLY generated the red errors before? Did and do any other method calls OF THE SAME OBJECT INSTANCE work correctly, before and now? What is the meaning of COM numeric returns (retvals) in each method call? Are they successes or failures? Have you ever gotten that message box before in any other contexts? I'm sorry but this isn't at all obvious from the listings you submit.
Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 01:35:36 PM
Mike,

Sage has three business objects for AR_Customer.

AR_Customer_ui
AR_Customer_bus
AR_Customer_svc

There is a lot of inheriting going on under the Sage 100 covers. The routines for declare INDEX, StartingKey and then a MoveNext are all in the SY_Service object which doesn't seem to be inherited in the AR_Customer_bus object. Why it works with VBScript still has me puzzled. In this example I include both AR_Customer_bus and AR_Customer_svc which doesn't cause any errors but I'm still not getting data from the customer record yet.

I recompiled with your latest change and no errors reported. I think ProvideX.Script takes care of object ref counting. I have never seen in the VBScript code anything about object count references.,

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. ' HomeDirOK = COM::CallByName(oscript, "Init", vbMethod, NT::RegRead("HKEY_CURRENT_USER\\Software\\ODBC\\ODBC.INI\\SOTAMAS90\\Directory") & "\\HOME")
  8. COM::CallByName(oScript,"Init",vbMethod,"C:\\Sage\\Sage 100 Advanced ERP\\MAS90\\HOME")
  9. osession = COM::CallByName(oscript, "NewObject", vbMethod, "SY_Session")
  10. UserOK = COM::CallByName(osession, "nSetUser", vbMethod, "JRS", "northstar")
  11. CompanyOK = COM::CallByName(osession, "nsetcompany", vbMethod, "ABC")
  12. DateOK = COM::CallByName(osession, "nSetDate", vbMethod, "A/R", "20171119")
  13. ModuleOK = COM::CallByName(osession, "nSetModule", vbMethod, "A/R")
  14. osec = COM::CallByName(osession, "nSetProgram", vbMethod, COM::CallByName(osession, "nLookupTask", vbMethod, "AR_Customer_ui"))
  15. ocust = COM::CallByName(oscript, "NewObject", vbMethod, "AR_Customer_bus", "VT_DISPATCH:" & osession)
  16. ocustkey = COM::CallByName(oscript, "NewObject", vbMethod, "AR_Customer_svc", "VT_DISPATCH:" & osession)
  17. retval = 0
  18. CustomerNo = ""
  19. CustomerName = ""
  20. City = ""
  21. State = ""
  22. TelephoneNo = ""
  23.  
  24. retval = COM::CallByName(ocustkey, "nSetIndex", vbMethod, "KNAME")
  25. retval = COM::CallByName(ocustkey, "nFind", vbMethod, "00ABC")
  26. retval = COM::CallByName(ocustkey, "nMoveNext", vbMethod)
  27. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerNo$", CustomerNo)
  28. retval = NT::MsgBox("CustomerNo: |" & CustomerNo & "|","DEBUG", "OK")
  29. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerName$", CustomerName)
  30. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "City$", City)
  31. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "State$", State)
  32. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "TelephoneNo$", TelephoneNo)
  33.  
  34. retval = NT::MsgBox("Customer:  " & CustomerNo & "  " & CustomerName & "  " & City & "  " & State & "  " & TelephoneNo, "PVXCOM Code Challenge", "OK")
  35. COM::CallByName(osession,"DropObject")
  36. COM::ReleaseObject(oscript)
  37.  


C:\ScriptBASIC\examples>sbwin comcustcc.sb
The number of arguments is: 1
CreateObject(ProvideX.Script)
CallByName 4 args
CallByName(obj=2950bc, method='Init', calltype=1 , comArgs=1)
CallByName 4 args
CallByName(obj=2950bc, method='NewObject', calltype=1 , comArgs=1)
return value from COM function was numeric: 2707932
CallByName 5 args
CallByName(obj=2951dc, method='nSetUser', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=2951dc, method='nsetcompany', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=2951dc, method='nSetDate', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=2951dc, method='nSetModule', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=2951dc, method='nLookupTask', calltype=1 , comArgs=1)
return value from COM function was numeric: 40000001
CallByName 4 args
CallByName(obj=2951dc, method='nSetProgram', calltype=1 , comArgs=1)
return value from COM function was numeric: 100006
CallByName 5 args
CallByName(obj=2950bc, method='NewObject', calltype=1 , comArgs=2)
return value from COM function was numeric: 2708076
CallByName 5 args
CallByName(obj=2950bc, method='NewObject', calltype=1 , comArgs=2)
return value from COM function was numeric: 2708220
CallByName 4 args
CallByName(obj=2952fc, method='nSetIndex', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=2952fc, method='nFind', calltype=1 , comArgs=1)
return value from COM function was numeric: 0
CallByName 3 args
CallByName(obj=2952fc, method='nMoveNext', calltype=1 , comArgs=0)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=29526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=29526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=29526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=29526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=29526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 2 args
CallByName(obj=2951dc, method='DropObject', calltype=1 , comArgs=0)

C:\ScriptBASIC\examples>

Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 12, 2017, 02:07:00 PM
No no John,

Please run it with the ..._bus object as you did before to see if the three red errors persist with this artificial manual AddRef() call!

COM languages usually do ref counting transparently under the hood. Both the server and client are COM languages while we, with our sb_engine.dll, are just a proxy -- a means to inform the client that the object of such and such type has been created at such and such address with an initial ref count of 1. The ref count is initially incremented by the COM server when the object is created and then modified (+/-) by the COM client as it consumes its methods and properties making occasional temporary assignments here and there in the process. When both server and client's references to that object all go out of scope and the ref count falls to 0, the object is destroyed automatically by the corresponding garbage collector.

If we try to add one more ref count manually, then when and where are we supposed to decrement it accordingly? Our proxy is a one-way pipe that passes the pointer value to but not fro!
Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 02:22:42 PM
That seemed to fix the GetIDsOfNames failed problem. (surprised!)

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_bus", "VT_DISPATCH:" & osession)
  15.  
  16. retval = 0
  17. sCustomerNo = ""
  18. sCustomerName = ""
  19. sCity = ""
  20. sState = ""
  21. sTelephoneNo = ""
  22.  
  23. retval = COM::CallByName(ocust, "nSetIndex", vbMethod, "KNAME")
  24. retval = COM::CallByName(ocust, "nFind", vbMethod,"")
  25. retval = COM::CallByName(ocust, "nMoveNext", vbMethod)
  26. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerNo$", sCustomerNo)
  27. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "CustomerName$", sCustomerName)
  28. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "City$", sCity)
  29. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "State$", sState)
  30. retval = COM::CallByName(ocust, "nGetValue", vbMethod, "TelephoneNo$", sTelephoneNo)
  31.  
  32. retval = NT::MsgBox("Customer:  " & sCustomerNo & "  " & sCustomerName & "  " & sCity & "  " & sState & "  " & sTelephoneNo, "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=c50bc, method='Init', calltype=1 , comArgs=1)
CallByName 4 args
CallByName(obj=c50bc, method='NewObject', calltype=1 , comArgs=1)
return value from COM function was numeric: 807388
CallByName 5 args
CallByName(obj=c51dc, method='nSetUser', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=c51dc, method='nsetcompany', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c51dc, method='nSetDate', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=c51dc, method='nSetModule', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=c51dc, method='nLookupTask', calltype=1 , comArgs=1)
return value from COM function was numeric: 40000001
CallByName 4 args
CallByName(obj=c51dc, method='nSetProgram', calltype=1 , comArgs=1)
return value from COM function was numeric: 100006
CallByName 5 args
CallByName(obj=c50bc, method='NewObject', calltype=1 , comArgs=2)
return value from COM function was numeric: 807532
CallByName 4 args
CallByName(obj=c526c, method='nSetIndex', calltype=1 , comArgs=1)
return value from COM function was numeric: 1
CallByName 4 args
CallByName(obj=c526c, method='nFind', calltype=1 , comArgs=1)
return value from COM function was numeric: 0
CallByName 3 args
CallByName(obj=c526c, method='nMoveNext', calltype=1 , comArgs=0)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 5 args
CallByName(obj=c526c, method='nGetValue', calltype=1 , comArgs=2)
return value from COM function was numeric: 1
CallByName 2 args
CallByName(obj=c51dc, method='DropObject', calltype=1 , comArgs=0)

C:\ScriptBASIC\examples>
Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 02:30:14 PM
Quote from:  Josť Roca
Forget my remark about increasing the reference count of the dispatch pointer because Dave's code for CallByName is not following the COM rules and it is not clearing the passed variants. It is just deleting the array of variants without calling VariantClear.

I'm confused as using AR_Customer_bus now works with the methods that failed before.

The problem I have now is I don't think Dave's CallByName knows how to return a value to one of its passed arguments. GetValue() method remains a "". (empty string)

Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 12, 2017, 03:00:03 PM
Hehe, no wonder ..._bus method calls now work as expected.

Jose's response is absolutely correct.

Dave's implementation is "half baked". It creates COM objects but it neither increments their initial ref count nor does it keep track of it later in sync with the client's garbage collection rules. Which means Sage is short of 1 ref count when VARIANTARG va goes out of scope and Sage's garbage collector kills the instance as soon as whichever assignments have been made on the receiving end of the very first method call all go out of scope too. Which, in its turn, means by the time we want to make a second call to the instance's methods it is already dead and non-existent!

Dave's code ought to be augmented with a meticulous ref count mechanism to match the Sage garbage collector's expectations. Else I'm afraid there won't be any chance for SB on Sage's scene.
Title: Re: Script BASIC COM
Post by: John on December 12, 2017, 03:18:22 PM
I agree!

I would still like to fix the issue of GetByName returning the successful GetValue argument to SB's passed initialized variable. Special type?

ProvideX COM has a CleanUp routine I think cleans up any reference count issues on the release side. I call ReleaseObject method calls to ProvideX as well. I think we just need to do what VBScript is doing when it calls the ProvideX.Script NewObject method.

The only thing I think is outstanding is getting values returned in method call arguments to update the SB passed argument acting as a place holder for it.

Title: Re: Script BASIC COM
Post by: Mike Lobanovsky on December 12, 2017, 03:50:43 PM
I think we just need to do what VBScript is doing when it calls the ProvideX.Script NewObject method.

No big deal, you think? :D

Quote
The only thing I think is outstanding is getting values returned in method call arguments to update the SB passed argument acting as a place holder for it.

I think these particulars would be easier to resolve with a full-fledged installation of both client and server and a matching test suite to work with. We can discuss it in a greater detail via email if you don't mind. 
Title: Re: Script BASIC COM
Post by: John on January 15, 2018, 10:22:35 PM
I wanted to post an update to where the SBCOM project is at this point. I would like to thank Mike for extending the interface to work with Sage BOI.


This is an example of using the Script BASIC multi-threaded application server (that runs as a Windows service) and Sage 100 BOI to generate an ABC demo company contact list in the launcher web control.

Code: Script BASIC
  1. ' BOI Customer Contact - Web Page
  2.  
  3. IMPORT cgi.sbi
  4. IMPORT boi.sbi
  5.  
  6. oscript = BOI::CREATE(:SET, "ProvideX.Script")
  7. BOI::CBN oScript, "Init", :CALL, "C:\\Sage\\Sage 100 Advanced ERP\\MAS90\\HOME"
  8. osession = BOI::CBN(oscript, "NewObject", :SET, "SY_Session")
  9. BOI::CBN osession, "nSetUser", :CALL, "JRS", "MyPassword"
  10. BOI::CBN osession, "nsetcompany", :CALL, "ABC"
  11. BOI::CBN osession, "nSetDate", :CALL, "A/R", "20171218"
  12. BOI::CBN osession, "nSetModule", :CALL, "A/R"
  13. ocust = BOI::CBN(oscript, "NewObject", :SET, "AR_Customer_svc", osession)
  14.  
  15. cgi::Header 200,"text/html"
  16. cgi::FinishHeader
  17.  
  18. PRINT """
  19. <html>
  20. <header>
  21. <title>Customer Contacts</title>
  22. </header>
  23. <body>
  24. <table>
  25. <center><h1>ABC - Customer Contacts</h1></center>
  26. <table style="width:100%">
  27.  <tr>
  28.    <th>Customer Number</th>
  29.    <th>Company Name</th>
  30.    <th>Phone Number</th>
  31.  </tr>
  32. """
  33.  
  34. BOI::CBN ocust,"nMoveFirst"
  35. DO UNTIL BOI::CBN(ocust, "nEOF", :GET)
  36.   PRINT "  <tr>\n"
  37.   PRINT "    <td>",BOI::CBN(ocust, "sCUSTOMERNO", :GET),"</td>\n"
  38.   PRINT "    <td>",BOI::CBN(ocust, "sCUSTOMERNAME", :GET),"</td>\n"
  39.   PRINT "    <td>",BOI::CBN(ocust, "sTELEPHONENO", :GET),"</td>\n"
  40.   PRINT "  </tr>\n"
  41.   BOI::CBN ocust, "nMoveNext"
  42. LOOP
  43. PRINT """
  44. </table>
  45. </body>
  46. </html>
  47. """
  48.  
  49. BOI::CBN ocust, "DropObject"
  50. BOI::CBN osession, "DropObject"
  51. BOI::RELEASE oscript
  52.  

(http://opensage.org/forum/pics/webcustview.png)