Author Topic: COM  (Read 5640 times)

0 Members and 1 Guest are viewing this topic.

JRS

  • Guest
COM
« on: October 10, 2020, 07:54:09 AM »
Charles,

Is it possible to add COM / OLE automation support to O2 as a library? You touched on this with DLLC.

Charles Pegge

  • Guest
Re: COM
« Reply #1 on: October 12, 2020, 05:19:27 AM »
Yes, John. We just need a COM automation developer to do it.

For low level COM, we have the SAPI5 text-to speech-example. It's the only COM I am familiar with, so other examples would be welcome.

JRS

  • Guest
Re: COM
« Reply #2 on: October 12, 2020, 05:37:28 AM »
Charles,

Could you send Jose Roca an e-mail inviting him back to participate in the forum. I don't know anyone that knows COM better than Jose. Mike when he was alive was my goto guy with COM.

Dave Zimmer's (SB COM ext. module author) CallByName COM/OLE automation interface is written C. I use VS2012 to maintain the interface. Maybe using that as a base for O2 COM automation is the way to go.
« Last Edit: October 12, 2020, 05:45:46 AM by John »

Brian Alvarez

  • Guest
Re: COM
« Reply #3 on: October 12, 2020, 06:42:48 AM »

  I hope im wrong... but i doubt Jose will Join in. I have reasons to think so, but i would like if he would.

JRS

  • Guest
Re: COM
« Reply #4 on: October 12, 2020, 07:08:02 AM »
If I'm the reason Jose isn't participating I wish to apologize if I said anything that was offending.  I really do miss him a lot.

JRS

  • Guest
Re: COM
« Reply #5 on: October 12, 2020, 09:31:04 AM »
Here is thread by Jose Roca on his FreeBasic OLE automation interface. Maybe this could be used as a base for a O2 automation interface library?

JR COM

I'm going to extract the CallByNane C code from the SB COM extention module and post it here. Maybe I can get some help porting it to O2.
« Last Edit: October 12, 2020, 09:51:41 AM by John »

JRS

  • Guest
Re: COM
« Reply #6 on: October 12, 2020, 10:47:11 AM »
Here is Dave Zimmer's code for COM/OLE automation with ScriptBasic. My gut says this shouldn't be too hard to convert to Oxygen Basic.

Code: C
  1. /*
  2.  Author:  David Zimmer <dzzie@yahoo.com>
  3.  Site:    http://sandsprite.com
  4.  
  5.  Notes: Not all COM types are currently handled, but enough to be useful
  6.         this is still a bit of work in progress. I will make additions as
  7.         I use it and find it necessary.
  8.  
  9.  Script Basic Declarations to use this extension:
  10.  
  11.                 declare sub CreateObject alias "CreateObject" lib "test.exe"
  12.                 declare sub CallByName alias "CallByName" lib "test.exe"
  13.                 declare sub ReleaseObject alias "ReleaseObject" lib "test.exe"
  14.         declare sub GetHostObject alias "GetHostObject" lib "sb_engine.dll"
  15.         declare sub GetHostString alias "GetHostString" lib "sb_engine.dll"
  16.         declare sub TypeName alias "TypeName" lib "sb_engine.dll"
  17.  
  18.                 const VbGet = 2
  19.                 const VbLet = 4
  20.                 const VbMethod = 1
  21.                 const VbSet = 8
  22.  
  23.  Example:
  24.  
  25.                 'you can load objects either by ProgID or CLSID
  26.                 'obj = CreateObject("SAPI.SpVoice")
  27.                 obj = CreateObject("{96749377-3391-11D2-9EE3-00C04F797396}")
  28.  
  29.                 if obj = 0 then
  30.                         print "CreateObject failed!\n"
  31.                 else
  32.                         CallByName(obj, "rate", VbLet, 2)
  33.                         CallByName(obj, "volume", VbLet, 60)
  34.                         CallByName(obj, "speak", VbMethod, "This is my test")
  35.                         ReleaseObject(obj)
  36.                 end if
  37.  
  38. */
  39.  
  40. #include <stdio.h>
  41. #include <list>
  42. #include <string>
  43.  
  44. #include <comdef.h>
  45. #include <AtlBase.h>
  46. #include <AtlConv.h>
  47. #include <atlsafe.h>
  48.  
  49. #include "basext.h"
  50. #include "vb.h"
  51.  
  52. int com_dbg = 0;
  53. int initilized=0;
  54. vbHostResolverCallback vbHostResolver = NULL;
  55.  
  56. pSupportTable g_pSt = NULL;
  57. #define EXPORT comment(linker, "/EXPORT:"__FUNCTION__"="__FUNCDNAME__)
  58.  
  59. int __stdcall SBCallBack(int EntryPoint)
  60. {
  61. #pragma EXPORT
  62.  
  63.   pSupportTable pSt = g_pSt;
  64.   VARIABLE FunctionResult;
  65.   int retVal;
  66.  
  67.   if(pSt==NULL) return -1;
  68.   besHOOK_CALLSCRIBAFUNCTION(EntryPoint, 0, 0, &FunctionResult);
  69.   retVal = FunctionResult->Value.lValue;
  70.   besRELEASE(FunctionResult);
  71.   return retVal;
  72. }
  73.  
  74. //vbCallType aligns with DISPATCH_XX values for Invoke
  75. enum vbCallType{ VbGet = 2, VbLet = 4, VbMethod = 1, VbSet = 8 };
  76. enum colors{ mwhite=15, mgreen=10, mred=12, myellow=14, mblue=9, mpurple=5, mgrey=7, mdkgrey=8 };
  77.  
  78. //char* to wide string
  79. LPWSTR __C2W(char *szString){
  80.         DWORD n;
  81.         char *sz = NULL;
  82.         LPWSTR ws= NULL;
  83.         if(*szString && szString){
  84.                 sz = strdup(szString);
  85.                 n = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, -1, NULL, 0);
  86.                 if(n){
  87.                         ws = (LPWSTR)malloc(n*2);
  88.                         MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, sz, -1, ws, n);
  89.                 }
  90.         }
  91.         free(sz);
  92.         return ws;
  93. }
  94.  
  95.  // BSTR to C String conversion
  96. char* __B2C(BSTR bString)
  97. {
  98.         int i;
  99.         int n = (int)SysStringLen(bString);
  100.         char *sz;
  101.         sz = (char *)malloc(n + 1);
  102.  
  103.         for(i = 0; i < n; i++){
  104.                 sz[i] = (char)bString[i];
  105.         }
  106.         sz[i] = 0;
  107.         return sz;
  108. }
  109.  
  110. // BSTR to std::String conversion
  111. std::string __B2S(BSTR bString)
  112. {
  113.         char *sz = __B2C(bString);
  114.         std::string tmp = sz;
  115.         free(sz);
  116.         return tmp;
  117. }
  118.  
  119. //script basic STRING type to char*
  120. char* GetCString(VARIABLE v){
  121.  
  122.         int slen;
  123.         char *s;
  124.     char* myCopy = NULL;
  125.  
  126.         s = STRINGVALUE(v);
  127.         slen = STRLEN(v);
  128.         if(slen==0) return strdup("");
  129.  
  130.         myCopy = (char*)malloc(slen+1);
  131.         if(myCopy==0) return 0;
  132.  
  133.         memcpy(myCopy,s, slen);
  134.         myCopy[slen]=0;
  135.         return myCopy;
  136.  
  137. }
  138.  
  139.  
  140. void color_printf(colors c, const char *format, ...)
  141. {
  142.         DWORD dwErr = GetLastError();
  143.         HANDLE hConOut = GetStdHandle( STD_OUTPUT_HANDLE );
  144.  
  145.         if(format){
  146.                 char buf[1024];
  147.                 va_list args;
  148.                 va_start(args,format);
  149.                 try{
  150.                          _vsnprintf(buf,1024,format,args);
  151.                          if(vbStdOut){
  152.                                  vbStdOut(cb_dbgout, buf, strlen(buf));
  153.                          }else{
  154.                                  SetConsoleTextAttribute(hConOut, c);
  155.                                  printf("%s",buf);
  156.                                  SetConsoleTextAttribute(hConOut,7);
  157.                          }
  158.                 }
  159.                 catch(...){}
  160.         }
  161.  
  162.         SetLastError(dwErr);
  163. }
  164.  
  165. HRESULT TypeName(IDispatch* pDisp, std::string *retVal)
  166. {
  167.     HRESULT hr = S_OK;
  168.         UINT count = 0;
  169.  
  170.     CComPtr<IDispatch> spDisp(pDisp);
  171.     if(!spDisp)
  172.         return E_INVALIDARG;
  173.  
  174.     CComPtr<ITypeInfo> spTypeInfo;
  175.     hr = spDisp->GetTypeInfo(0, 0, &spTypeInfo);
  176.  
  177.     if(SUCCEEDED(hr) && spTypeInfo)
  178.     {
  179.         CComBSTR funcName;
  180.         hr = spTypeInfo->GetDocumentation(-1, &funcName, 0, 0, 0);
  181.         if(SUCCEEDED(hr) && funcName.Length()> 0 )
  182.         {
  183.           char* c = __B2C(funcName);
  184.                   *retVal = c;
  185.                   free(c);
  186.         }        
  187.     }
  188.  
  189.     return hr;
  190.  
  191. }
  192.  
  193. VARIANT __stdcall SBCallBackEx(int EntryPoint, VARIANT *pVal)
  194. {
  195. #pragma EXPORT
  196.  
  197.   pSupportTable pSt = g_pSt;
  198.   VARIABLE FunctionResult;
  199.   _variant_t vRet;
  200.  
  201.   if(pSt==NULL){
  202.           MessageBox(0,"pSupportTable is not set?","",0);
  203.           return vRet.Detach();
  204.   }
  205.  
  206.     USES_CONVERSION;
  207.         char buf[1024]={0};
  208.     HRESULT hr;
  209.         long lResult;
  210.         long lb;
  211.         long ub;
  212.     SAFEARRAY *pSA = NULL;
  213.  
  214.         //we only accept variant arrays..
  215.         if (V_VT(pVal) == (VT_ARRAY | VT_VARIANT | VT_BYREF)) //24588
  216.                 pSA = *(pVal->pparray);
  217.         //else if (V_ISARRAY(pVal) && V_ISBYREF(pVal)) //array of longs here didnt work out maybe latter
  218.         //      pSA = *(pVal->pparray);
  219.         else
  220.         {
  221.                 if (V_VT(pVal) == (VT_ARRAY | VT_VARIANT))
  222.                         pSA = pVal->parray;
  223.                 else
  224.                         return vRet.Detach();//"Type Mismatch [in] Parameter."
  225.         };
  226.  
  227.     long dim = SafeArrayGetDim(pSA);
  228.         if(dim != 1) return vRet.Detach();
  229.  
  230.         lResult = SafeArrayGetLBound(pSA,1,&lb);
  231.         lResult = SafeArrayGetUBound(pSA,1,&ub);
  232.  
  233.         lResult=SafeArrayLock(pSA);
  234.     if(lResult) return vRet.Detach();
  235.  
  236.     _variant_t vOut;
  237.         _bstr_t cs;
  238.  
  239.         int sz = ub-lb+1;
  240.     VARIABLE pArg = besNEWARRAY(0,sz);
  241.  
  242.         //here we proxy the array of COM types into the array of script basic types element by element.
  243.         //      note this we only support longs and strings. floats will be rounded, objects converted to objptr()
  244.         //  bytes and integers are ok too..basically just not float and currency..which SB doesnt support anyway..
  245.     for (long l=lb; l<=ub; l++) {
  246.                 if( SafeArrayGetElement(pSA, &l, &vOut) == S_OK ){
  247.                         if(vOut.vt == VT_BSTR){
  248.                                 char* cstr = __B2C(vOut.bstrVal);
  249.                                 int slen = strlen(cstr);
  250.                                 pArg->Value.aValue[l] = besNEWMORTALSTRING(slen);
  251.                                 memcpy(STRINGVALUE(pArg->Value.aValue[l]),cstr,slen);
  252.                                 free(cstr);
  253.                         }
  254.                         else{
  255.                                 if(vOut.vt == VT_DISPATCH){
  256.                                         //todo register handle? but how do we know the lifetime of it..
  257.                                         //might only be valid until this exits, or forever?
  258.                                 }
  259.                                 pArg->Value.aValue[l] = besNEWMORTALLONG;
  260.                                 LONGVALUE(pArg->Value.aValue[l]) = vOut.lVal;
  261.                         }
  262.                 }
  263.     }
  264.  
  265.   lResult=SafeArrayUnlock(pSA);
  266.   if (lResult) return vRet.Detach();
  267.  
  268.   besHOOK_CALLSCRIBAFUNCTION(EntryPoint,
  269.                                                          pArg->Value.aValue,
  270.                              sz,
  271.                              &FunctionResult);
  272.  
  273.   for (long l=0; l <= sz; l++) {
  274.          besRELEASE(pArg->Value.aValue[l]);
  275.      pArg->Value.aValue[l] = NULL;
  276.   }
  277.        
  278.   if(FunctionResult->vType == VTYPE_STRING){
  279.         char* myStr = GetCString(FunctionResult);
  280.         vRet.SetString(myStr);
  281.         free(myStr);
  282.   }
  283.   else{
  284.           switch( TYPE(FunctionResult) )
  285.           {      
  286.                 case VTYPE_DOUBLE:
  287.                 case VTYPE_ARRAY:
  288.                 case VTYPE_REF:
  289.                                 MessageBoxA(0,"Arguments of script basic types [double, ref, array] not supported","Error",0);
  290.                                 break;
  291.                 default:
  292.                                 vRet = LONGVALUE(FunctionResult);
  293.           }
  294.   }
  295.  
  296.   besRELEASE(pArg);
  297.   besRELEASE(FunctionResult);
  298.  
  299.   return vRet.Detach();
  300. }
  301.  
  302. //note the braces..required so if(x)RETURN0(msg) uses the whole blob
  303. //should this be goto cleanup instead of return 0?
  304. #define RETURN0(msg) {if(com_dbg) color_printf(colors::mred, "%s\n", msg); \
  305.                          LONGVALUE(besRETURNVALUE) = 0; \
  306.                                          goto cleanup;}
  307.  
  308.  
  309.  
  310. besFUNCTION(TypeName)
  311.  
  312.         VARIABLE Argument ;
  313.         char* unk = "Failed";
  314.         besRETURNVALUE = besNEWMORTALLONG;
  315.  
  316.         if( besARGNR != 1) RETURN0("TypeName takes one argument!")
  317.  
  318.         Argument = besARGUMENT(1);
  319.         besDEREFERENCE(Argument);
  320.  
  321.         if( TYPE(Argument) != VTYPE_LONG) RETURN0("TypeName requires a long argument")
  322.         if( LONGVALUE(Argument) == 0) RETURN0("TypeName(NULL) called")
  323.         IDispatch* IDisp = (IDispatch*)LONGVALUE(Argument);
  324.        
  325.         try{
  326.                 std::string retVal;
  327.                 if(TypeName(IDisp, &retVal) == S_OK){
  328.                         besALLOC_RETURN_STRING(retVal.length());
  329.                         memcpy(STRINGVALUE(besRETURNVALUE),retVal.c_str(),retVal.length());
  330.                 }else{
  331.                         besALLOC_RETURN_STRING(strlen(unk));
  332.                         memcpy(STRINGVALUE(besRETURNVALUE),unk,strlen(unk));
  333.                 }
  334.         }catch(...){
  335.                 RETURN0("Invalid IDisp pointer?")
  336.         }
  337.  
  338. cleanup:
  339.         return 0;
  340.  
  341. besEND
  342.  
  343. //Object GetHostObject("Form1")
  344. //this is for embedded hosts, so script clients can dynamically look up obj pointers
  345. //for use with teh COM functions. Instead of the MS Script host design of AddObject
  346. //here we allow the script to query values from a host resolver. Its easier than
  347. //trying to mess with the internal Symbol tables, and cleaner than editing an include
  348. //script on the fly every single launch to add global variables which would then show up
  349. //in the debug pane. this function can be used for retrieving any long value
  350. besFUNCTION(GetHostObject)
  351.   int retVal=0;
  352.   int slen;
  353.   char* myCopy = NULL;
  354.   VARIABLE Argument;
  355.   besRETURNVALUE = besNEWMORTALLONG;
  356.  
  357.   if( besARGNR != 1) RETURN0("GetHostObject takes one argument!")
  358.  
  359.   Argument = besARGUMENT(1);
  360.   besDEREFERENCE(Argument);
  361.  
  362.   if( TYPE(Argument) != VTYPE_STRING) RETURN0("GetHostObject requires a string argument")
  363.   if( STRLEN(Argument) > 1000) RETURN0("GetHostObject argument to long")
  364.  
  365.   myCopy = GetCString(Argument);
  366.   if(myCopy==0) RETURN0("malloc failed low mem")
  367.  
  368.   if(vbHostResolver==NULL) RETURN0("GetHostObject requires vbHostResolver callback to be set")
  369.        
  370.   retVal = vbHostResolver(myCopy, strlen(myCopy), 0);
  371.  
  372. cleanup:
  373.     LONGVALUE(besRETURNVALUE) = retVal;    
  374.         if(myCopy) free(myCopy);
  375.         return 0;
  376.  
  377. besEND
  378.  
  379. //as above but for retrieving strings up to 1024 chars long
  380. besFUNCTION(GetHostString)
  381.   int retVal=0;
  382.   int slen=0;
  383.   char* myCopy = NULL;
  384.   char buf[1026];
  385.   VARIABLE Argument;
  386.   besRETURNVALUE = besNEWMORTALLONG;
  387.  
  388.   if( besARGNR != 1) RETURN0("GetHostString takes one argument!")
  389.  
  390.   Argument = besARGUMENT(1);
  391.   besDEREFERENCE(Argument);
  392.  
  393.   if( TYPE(Argument) != VTYPE_STRING) RETURN0("GetHostString requires a string argument")
  394.   if( STRLEN(Argument) > 1000) RETURN0("GetHostString argument to long")
  395.  
  396.   myCopy = GetCString(Argument);
  397.   if(myCopy==0) RETURN0("malloc failed low mem")
  398.  
  399.   if(vbHostResolver==NULL) RETURN0("GetHostStringt requires vbHostResolver callback to be set")
  400.        
  401.   //we are actually going to use our own fixed size buffer for this in case its a string value to be returned..
  402.   strcpy(buf, myCopy);
  403.   retVal = vbHostResolver(buf, strlen(buf), 1024);
  404.   slen = strlen(buf);
  405.  
  406. cleanup:
  407.     besALLOC_RETURN_STRING(slen);
  408.     if(slen > 0) memcpy(STRINGVALUE(besRETURNVALUE),buf,slen);
  409.         if(myCopy) free(myCopy);
  410.         return 0;
  411.  
  412. besEND
  413.  
  414.  
  415. //ReleaseObject(obj)
  416. besFUNCTION(ReleaseObject)
  417.  
  418.         VARIABLE Argument;
  419.         besRETURNVALUE = besNEWMORTALLONG;
  420.  
  421.         if( besARGNR != 1) RETURN0("ReleaseObject takes one argument!")
  422.  
  423.         Argument = besARGUMENT(1);
  424.         besDEREFERENCE(Argument);
  425.  
  426.         if( TYPE(Argument) != VTYPE_LONG) RETURN0("ReleaseObject requires a long argument")
  427.         if( LONGVALUE(Argument) == 0) RETURN0("ReleaseObject(NULL) called")
  428.         IDispatch* IDisp = (IDispatch*)LONGVALUE(Argument);
  429.        
  430.         try{
  431.                 IDisp->Release();
  432.         }catch(...){
  433.                 RETURN0("Invalid IDisp pointer?")
  434.         }
  435.  
  436.         Argument->Value.lValue = 0;
  437.  
  438. cleanup:
  439.         return 0;
  440.  
  441. besEND
  442.  
  443. //Object CreateObject("ProgID")
  444. besFUNCTION(CreateObject)
  445.   int i;
  446.   int slen;
  447.   char *s;
  448.   char* myCopy = NULL;
  449.   LPWSTR wStr = NULL;
  450.   VARIABLE Argument;
  451.   besRETURNVALUE = besNEWMORTALLONG;
  452.   CLSID     clsid;
  453.   HRESULT       hr;
  454.   IDispatch *IDisp = NULL;
  455.  
  456.   if(com_dbg) color_printf(colors::myellow, "The number of arguments is: %ld\n",besARGNR);
  457.  
  458.   if( besARGNR != 1) RETURN0("CreateObject takes one argument!")
  459.  
  460.   Argument = besARGUMENT(1);
  461.   besDEREFERENCE(Argument);
  462.  
  463.   if( TYPE(Argument) != VTYPE_STRING) RETURN0("CreateObject requires a string argument")
  464.  
  465.   if(!initilized){
  466.           CoInitialize(NULL);
  467.           initilized = 1;
  468.   }
  469.  
  470.   myCopy = GetCString(Argument);
  471.   if(myCopy==0) RETURN0("malloc failed low mem")
  472.  
  473.   wStr = __C2W(myCopy);
  474.   if(wStr==0) RETURN0("unicode conversion failed")
  475.  
  476.   if(com_dbg) color_printf(colors::myellow,"CreateObject(%s)\n", myCopy);
  477.  
  478.   if(myCopy[0] == '{'){
  479.         hr = CLSIDFromString( wStr , &clsid); //its a string CLSID directly
  480.   }else{
  481.         hr = CLSIDFromProgID( wStr , &clsid); //its a progid
  482.   }
  483.  
  484.   if( hr != S_OK  ) RETURN0("Failed to get clsid")
  485.  
  486.   hr =  CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, IID_IDispatch,(void**)&IDisp);
  487.   if ( hr != S_OK ){
  488.           //ok maybe its an activex exe..
  489.           hr =  CoCreateInstance( clsid, NULL, CLSCTX_LOCAL_SERVER, IID_IDispatch,(void**)&IDisp);
  490.           if ( hr != S_OK ) RETURN0("CoCreateInstance failed does object support IDispatch?")
  491.   }
  492.  
  493.   //todo: keep track of valid objects we create for release/call sanity check latter?
  494.   //      tracking would break operation though if an embedded host used setvariable to add an obj reference..
  495.   //      unless it used an AddObject(name,pointer) method to add it to the tracker..
  496.   //      how else can we know if a random number is a valid com object other than tracking?
  497.   //      handled with a try/catch block in CallByName right now
  498.  
  499. cleanup:
  500.         LONGVALUE(besRETURNVALUE) = (int)IDisp;    
  501.         if(myCopy) free(myCopy);
  502.         if(wStr)   free(wStr);
  503.         return 0;
  504.  
  505. besEND
  506.  
  507.  
  508. // the idea behind this one is that we can use a string to embed a type specifier
  509. // to explicitly declare and cast a variable to the type we want such as "VT_I2:2"
  510. //
  511. // in testing with VB6 however, if we pass .vt = VT_I4 when vb6 expects a VT_I1 (char)
  512. // it works as long as the value is < 255, also works with VT_BOOL
  513. //
  514. // do we really need this function ? I prefer less complexity if possible.
  515. //
  516. // Note: there are many COM types, I have no plans to cover them all
  517.  
  518. bool HandleSpecial(VARIANTARG* va, char* str){
  519.  
  520.         return false; //disabled for now see notes above..
  521.  
  522.         if(str==0) return false;
  523.  
  524.         std::string s = str;
  525.          
  526.         if(s.length() < 3) return false;
  527.         if(s.substr(0,3) != "VT_") return false;
  528.        
  529.         int pos = s.find(":",0);
  530.         if(pos < 1) return false;
  531.  
  532.         std::string cmd = s.substr(0,pos);
  533.         if(s.length() < pos+2) return false;
  534.  
  535.         s = s.substr(pos+1);
  536.  
  537.         //todo implement handling of these types (there are many more than this)
  538.         if(cmd == "VT_I1"){
  539.         }else if(cmd == "VT_I2"){
  540.         }else if(cmd == "VT_I8"){
  541.     }else if(cmd == "VT_BOOL"){
  542.         }
  543.        
  544.         return true;
  545. }
  546.  
  547. /*
  548.     arguments in [] are optional, default calltype = method
  549.         callbyname object, "procname", [vbcalltype = VbMethod], [arg0], [arg1] ...
  550. */     
  551.  
  552. besFUNCTION(CallByName)
  553.  
  554.   int i;
  555.   int slen;
  556.   char *s;
  557.   int com_args = 0;
  558.   char* myCopy = NULL;
  559.   LPWSTR wMethodName = NULL;
  560.   vbCallType CallType = VbMethod;
  561.   std::list<BSTR> bstrs;
  562.   VARIANTARG* pvarg = NULL;
  563.  
  564.   VARIABLE arg_obj;
  565.   VARIABLE arg_procName;
  566.   VARIABLE arg_CallType;
  567.  
  568.   besRETURNVALUE = besNEWMORTALLONG;
  569.   LONGVALUE(besRETURNVALUE) = 0;
  570.  
  571.   g_pSt = pSt;
  572.  
  573.   if(com_dbg) color_printf(colors::myellow,"CallByName %ld args\n",besARGNR);
  574.  
  575.   if(besARGNR < 2) RETURN0("CallByName requires at least 2 args..")
  576.  
  577.   arg_obj = besARGUMENT(1);
  578.   besDEREFERENCE(arg_obj);
  579.  
  580.   if( TYPE(arg_obj) != VTYPE_LONG) RETURN0("CallByName first argument must be a long")
  581.  
  582.   arg_procName = besARGUMENT(2);
  583.   besDEREFERENCE(arg_procName);
  584.  
  585.   if( TYPE(arg_procName) != VTYPE_STRING) RETURN0("CallByName second argument must be a string")
  586.  
  587.   if( besARGNR >= 3 ){
  588.     arg_CallType = besARGUMENT(3);
  589.     besDEREFERENCE(arg_CallType);
  590.         CallType = (vbCallType)LONGVALUE(arg_CallType);
  591.   }
  592.  
  593.   myCopy = GetCString(arg_procName);
  594.   if(myCopy==0) RETURN0("malloc failed low mem")
  595.  
  596.   wMethodName = __C2W(myCopy);
  597.   if(wMethodName==0) RETURN0("unicode conversion failed")
  598.  
  599.   if( LONGVALUE(arg_obj) == 0) RETURN0("CallByName(NULL) called")
  600.   IDispatch* IDisp = (IDispatch*)LONGVALUE(arg_obj);
  601.   DISPID  dispid; // long integer containing the dispatch ID
  602.   HRESULT hr;
  603.  
  604.   // Get the Dispatch ID for the method name,
  605.   // try block is in case client passed in an invalid pointer
  606.   try{
  607.           hr = IDisp->GetIDsOfNames(IID_NULL, &wMethodName, 1, LOCALE_USER_DEFAULT, &dispid);
  608.           if( FAILED(hr) ) RETURN0("GetIDsOfNames failed")
  609.   }
  610.   catch(...){
  611.           RETURN0("Invalid IDisp pointer?")
  612.   }
  613.          
  614.   VARIANT    retVal;
  615.   DISPPARAMS dispparams;
  616.   memset(&dispparams, 0, sizeof(dispparams));
  617.  
  618.   com_args = besARGNR - 3;
  619.   if(com_args < 0) com_args = 0;
  620.    
  621.   if(com_dbg) color_printf(colors::myellow,"CallByName(obj=%x, method='%s', calltype=%d , comArgs=%d)\n", LONGVALUE(arg_obj), myCopy, CallType, com_args);
  622.  
  623.   // Allocate memory for all VARIANTARG parameters.
  624.   if(com_args > 0){
  625.          pvarg = new VARIANTARG[com_args];
  626.          if(pvarg == NULL) RETURN0("failed to alloc VARIANTARGs")
  627.   }
  628.  
  629.   dispparams.rgvarg = pvarg;
  630.   if(com_args > 0) memset(pvarg, 0, sizeof(VARIANTARG) * com_args);
  631.          
  632.   dispparams.cArgs = com_args;  // num of args function takes
  633.   dispparams.cNamedArgs = 0;
  634.  
  635.   /* map in argument values and types    ->[ IN REVERSE ORDER ]<-    */
  636.   for(int i=0; i < com_args; i++){
  637.           VARIABLE arg_x;              
  638.           arg_x = besARGUMENT(3 + com_args - i);
  639.           besDEREFERENCE(arg_x);
  640.  
  641.                 switch( TYPE(arg_x) ){ //script basic type to COM variant type
  642.  
  643.                           case VTYPE_DOUBLE:
  644.                           case VTYPE_ARRAY:
  645.                           case VTYPE_REF:
  646.                                 RETURN0("Arguments of script basic types [double, ref, array] not supported")
  647.                                 break;
  648.  
  649.                           case VTYPE_LONG:
  650.                                 pvarg[i].vt = VT_I4;
  651.                                 pvarg[i].lVal = LONGVALUE(arg_x);
  652.                                 break;
  653.                          
  654.                           case VTYPE_STRING:
  655.                                 char* myStr = GetCString(arg_x);
  656.                                
  657.                                 //peek at data and see if an explicit VT_ type was specified.. scriptbasic only supports a few types
  658.                                 if( !HandleSpecial(&pvarg[i], myStr) ){
  659.                                         //nope its just a standard string type
  660.                                         LPWSTR wStr = __C2W(myStr);
  661.                                         BSTR bstr = SysAllocString(wStr);
  662.                                         bstrs.push_back(bstr); //track these to free after call to prevent leak
  663.                                         pvarg[i].vt = VT_BSTR;
  664.                                         pvarg[i].bstrVal = bstr;
  665.                                         free(myStr);
  666.                                         free(wStr);
  667.                                 }
  668.  
  669.                                 break;                   
  670.                                
  671.           }
  672.  
  673.   }
  674.    
  675.   //invoke should not need a try catch block because IDisp is already known to be ok and COM should only return a hr result?
  676.  
  677.   //property put gets special handling..
  678.   if(CallType == VbLet){
  679.             DISPID mydispid = DISPID_PROPERTYPUT;
  680.         dispparams.rgdispidNamedArgs = &mydispid;
  681.                 dispparams.cNamedArgs = 1;
  682.                 hr=IDisp->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, CallType, &dispparams, NULL, NULL, NULL); //no return value arg
  683.                 if( FAILED(hr) ) RETURN0("Invoke failed")
  684.                 goto cleanup;
  685.   }
  686.  
  687.   hr=IDisp->Invoke( dispid, IID_NULL, LOCALE_USER_DEFAULT, CallType, &dispparams, &retVal, NULL, NULL);
  688.   if( FAILED(hr) ) RETURN0("Invoke failed")
  689.  
  690.   char* cstr = 0;
  691.   //map in return value to scriptbasic return val
  692.   switch(retVal.vt)
  693.   {
  694.         case VT_EMPTY: break;
  695.  
  696.         case VT_BSTR:
  697.  
  698.             cstr = __B2C(retVal.bstrVal);
  699.                 slen = strlen(cstr);
  700.                 if(com_dbg) color_printf(colors::myellow,"return value from COM function was string: %s\n", cstr);
  701.                 besALLOC_RETURN_STRING(slen);
  702.                 memcpy(STRINGVALUE(besRETURNVALUE),cstr,slen);
  703.                 free(cstr);
  704.                 break;
  705.  
  706.         case VT_I4:  /* this might be being really lazy but at least with VB6 it works ok.. */
  707.         case VT_I2:
  708.         case VT_I1:
  709.     case VT_BOOL:
  710.         case VT_UI1:
  711.         case VT_UI2:
  712.         case VT_UI4:
  713.         case VT_I8:
  714.         case VT_UI8:
  715.         case VT_INT:
  716.         case VT_UINT:
  717.         case VT_DISPATCH:
  718.  
  719.                 if(com_dbg) color_printf(colors::myellow,"return value from COM function was numeric: %d\n", retVal.lVal);
  720.         LONGVALUE(besRETURNVALUE) = retVal.lVal;
  721.                 break;
  722.  
  723.         default:
  724.                 color_printf(colors::mred,"currently unsupported VT return type: %x\n", retVal.vt);
  725.                 break;
  726.   }
  727.  
  728.  
  729. cleanup:
  730.  
  731.   for (std::list<BSTR>::iterator it=bstrs.begin(); it != bstrs.end(); ++it) SysFreeString(*it);
  732.   if(pvarg)       delete pvarg;
  733.   if(wMethodName) free(wMethodName); //return0 maybe should goto cleanup cause these would leak
  734.   if(myCopy)      free(myCopy);
  735.   return 0;
  736.  
  737. besEND
  738.  

JRS

  • Guest
Re: COM
« Reply #7 on: October 12, 2020, 02:13:56 PM »
Here is your COM code in the current distribution. It seems you already have the functions needed to implement the CallByName (invoke) method. (OLE Automation)

Code: OxygenBasic
  1.  
  2.   #case capital
  3.  
  4.   typedef struct _guid
  5.   {
  6.     a as dword
  7.     b as word
  8.     c as word
  9.     d[8] as byte
  10.   } guid, GUID
  11.  
  12.   'typedef struct {
  13.  'dword d1
  14.  'word  w1,w2
  15.  'byte  b1,b2,b3,b4,b5,b6,b7,b8
  16.  '} GUID,CLSID,*refiid
  17.  '
  18.  '#define DEFINE_GUID(gu,d1,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) GUID gu<=(d1,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8)
  19.  
  20.  
  21.   'standalone definitions ad hoc
  22.  
  23.   #define __in
  24.   #define __out
  25.   #define __in_opt      optional
  26.   #define __out_opt     optional
  27.   #define __RPC__in_opt optional
  28.  
  29.   typedef int       HRESULT
  30.   typedef sys       HANDLE,HWND,WMSG,WPARAM,LPARAM
  31.   typedef dword     DWORD,ULONG,UINT
  32.   typedef qword     ULONGLONG
  33.   typedef bool      BOOL
  34.   typedef wchar     *LPCWSTR
  35.   typedef word      USHORT
  36.   typedef void      *LPVOID
  37.   typedef guid      *REFCLSID, *REFIID
  38.   typedef void      *LPVOID,*COSERVERINFO
  39.   typedef wbstring  LPOLESTR
  40.   typedef uint      LCID
  41.   typedef uint      DISPID
  42.  
  43.   typedef void      ITypeInfo 'to be defined
  44.  
  45.  
  46.   '
  47.  typedef void SPNOTIFYCALLBACK, ISpNotifyCallback, ISpNotifySink
  48.   typedef void ISpObjectToken,IStream,ISpStreamFormat,SPVOICESTATUS,SPVPRIORITY,SPEVENTENUM      
  49.   typedef void SPEVENT,SPEVENTSOURCEINFO
  50.  
  51.   'typedef sys SPEVENT,SPEVENTSOURCEINFO
  52.  
  53.   uses com\VariantUtil
  54.   '
  55.  typedef VARIANT VARIANTARG
  56.  
  57.   typedef struct tagDISPPARAMS {
  58.   VARIANTARG *rgvarg
  59.   DISPID     *rgdispidNamedArgs
  60.   UINT       cArgs
  61.   UINT       cNamedArgs
  62.   } DISPPARAMS
  63.  
  64.   typedef bstr BSTR
  65.   typedef dword *ULONG_PTR
  66.  
  67.    typedef struct tagEXCEPINFO {
  68.    WORD wCode
  69.    WORD wReserved
  70.    BSTR bstrSource
  71.    BSTR bstrDescription
  72.    BSTR bstrHelpFile
  73.    DWORD dwHelpContext
  74.    ULONG_PTR pvReserved
  75.    ULONG_PTR pfnDeferredFillIn
  76.    HRESULT scode
  77.    } EXCEPINFO
  78.  
  79.  
  80.   'from Unknwn.h
  81.  
  82.   extern virtual
  83.  
  84.   --------------
  85.   class IUnknown
  86.   ==============
  87.  
  88.     HRESULT QueryInterface(REFIID id, pvObject* ppv)
  89.     ULONG   AddRef()
  90.     ULONG   Release()
  91.  
  92.   end class
  93.  
  94.   typedef IUnknown *LPUNKNOWN
  95.  
  96.  
  97.   ---------------
  98.   class IDispatch
  99.   ===============
  100.  
  101.   extends IUnknown
  102.  
  103.   HRESULT GetIDsOfNames(
  104.   REFIID   riid,
  105.   LPOLESTR *rgszNames,
  106.   UINT     cNames,
  107.   LCID     lcid,
  108.   DISPID   *rgDispId)
  109.  
  110.   HRESULT GetTypeInfo(
  111.   UINT      iTInfo,
  112.   LCID      lcid,
  113.   ITypeInfo **ppTInfo)
  114.  
  115.   HRESULT GetTypeInfoCount(
  116.   UINT *pctinfo)
  117.  
  118.   HRESULT Invoke(
  119.   DISPID     dispIdMember,
  120.   REFIID     riid,
  121.   LCID       lcid,
  122.   WORD       wFlags,
  123.   DISPPARAMS *pDispParams,
  124.   VARIANT    *pVarResult,
  125.   EXCEPINFO  *pExcepInfo,
  126.   UINT       *puArgErr)
  127.  
  128.   end class
  129.  
  130. '#recordof idispatch
  131.  
  132.  
  133.  
  134.   extern lib "ole32.dll"
  135.  
  136.  
  137.   HRESULT CoInitialize(
  138.     __in_opt  LPVOID pvReserved
  139.   );
  140.  
  141.  
  142.   void CoUninitialize(void);
  143.  
  144.  
  145.  
  146.   HRESULT CoCreateInstance(
  147.     __in   REFCLSID rclsid,
  148.     __in   LPUNKNOWN pUnkOuter,
  149.     __in   DWORD dwClsContext,
  150.     __in   REFIID riid,
  151.     __out  LPVOID *ppv
  152.   )
  153.  
  154.  
  155.  
  156.   HRESULT CoGetClassObject(
  157.     __in      REFCLSID rclsid,
  158.     __in      DWORD dwClsContext,
  159.     __in_opt  COSERVERINFO pServerInfo,
  160.     __in      REFIID riid,
  161.     __out     LPVOID *ppv
  162.   );
  163.  
  164.  
  165.   'HRESULT __stdcall DllGetClassObject(
  166.  '  __in   REFCLSID rclsid,
  167.  '  __in   REFIID riid,
  168.  '  __out  LPVOID *ppv
  169.  ');
  170.  
  171.   'HRESULT __stdcall DllCanUnloadNow(void);
  172.  
  173.  
  174.  
  175.   HRESULT CoRegisterClassObject(
  176.     __in   REFCLSID rclsid,
  177.     __in   LPUNKNOWN pUnk,
  178.     __in   DWORD dwClsContext,
  179.     __in   DWORD flags,
  180.     __out  LPDWORD lpdwRegister
  181.   );
  182.  
  183.  
  184.  
  185.   HRESULT CoRevokeClassObject(
  186.     __in  DWORD dwRegister
  187.   );
  188.  
  189.  
  190.  
  191.  
  192.   end extern
  193.  
  194.  
  195.   'CLSCTX_INPROC_SERVER            = 0x1
  196.  'CLSCTX_INPROC_HANDLER           = 0x2
  197.  'CLSCTX_LOCAL_SERVER             = 0x4
  198.  'CLSCTX_INPROC_SERVER16          = 0x8
  199.  'CLSCTX_REMOTE_SERVER            = 0x10
  200.  '
  201.  #define context 7
  202.  
  203.   sys Err
  204.  

Code: OxygenBasic
  1. #case capital
  2.  
  3. typedef quad  longlong, LONGLONG
  4. typedef qword ulonglong, ULONGLONG
  5. typedef void *pvoid, PVOID
  6. typedef int SCODE
  7. typedef double DATE
  8. typedef word USHORT
  9. typedef short SHORT,VARIANT_BOOL
  10. typedef byte BYTE
  11. typedef char CHAR
  12. typedef word WORD
  13. typedef long LONG
  14. typedef int INT
  15. typedef uint UINT
  16. typedef dword ULONG
  17. typedef float FLOAT
  18. typedef double DOUBLE
  19. typedef bstring BSTR
  20.  
  21. typedef void IUnknown,IDispatch
  22. typedef void IRecordInfo
  23.  
  24.  
  25. type DECIMAL
  26.    WORD wReserved
  27.    BYTE scale
  28.    BYTE sign
  29.    ULONG Hi32
  30.    ULONGLONG Lo64
  31. end type
  32.  
  33. type CY
  34.   LONGLONG int64
  35. end type
  36.  
  37. type SAFEARRAYBOUND
  38.    ULONG cElements
  39.    LONG lLbound
  40. end type
  41.  
  42. type SAFEARRAY
  43.   USHORT         cDims
  44.   USHORT         fFeatures
  45.   ULONG          cbElements
  46.   ULONG          cLocks
  47.   PVOID          pvData
  48.   SAFEARRAYBOUND rgsabound[1]
  49. end type
  50.  
  51.  
  52. enum VARTYPE {
  53.    VT_EMPTY = 0x0000,
  54.    VT_NULL = 0x0001,
  55.    VT_I2 = 0x0002,
  56.    VT_I4 = 0x0003,
  57.    VT_R4 = 0x0004,
  58.    VT_R8 = 0x0005,
  59.    VT_CY = 0x0006,
  60.    VT_DATE = 0x0007,
  61.    VT_BSTR = 0x0008,
  62.    VT_DISPATCH = 0x0009,
  63.    VT_ERROR = 0x000A,
  64.    VT_BOOL = 0x000B,
  65.    VT_VARIANT = 0x000C,
  66.    VT_UNKNOWN = 0x000D,
  67.    VT_DECIMAL = 0x000E,
  68.    VT_I1 = 0x0010,
  69.    VT_UI1 = 0x0011,
  70.    VT_UI2 = 0x0012,
  71.    VT_UI4 = 0x0013,
  72.    VT_I8 = 0x0014,
  73.    VT_UI8 = 0x0015,
  74.    VT_INT = 0x0016,
  75.    VT_UINT = 0x0017,
  76.    VT_VOID = 0x0018,
  77.    VT_HRESULT = 0x0019,
  78.    VT_PTR = 0x001A,
  79.    VT_SAFEARRAY = 0x001B,
  80.    VT_CARRAY = 0x001C,
  81.    VT_USERDEFINED = 0x001D,
  82.    VT_LPSTR = 0x001E,
  83.    VT_LPWSTR = 0x001F,
  84.    VT_RECORD = 0x0024,
  85.    VT_INT_PTR = 0x0025,
  86.    VT_UINT_PTR = 0x0026,
  87.    VT_ARRAY = 0x2000,
  88.    VT_BYREF = 0x4000
  89. }
  90.  
  91.  
  92. type VARIANT
  93.   DECIMAL decVal
  94.   =
  95.   WORD    vt 'VARTYPE
  96.  WORD    wReserved1
  97.   WORD    wReserved2
  98.   WORD    wReserved3
  99.   + 8
  100.   PVOID       pvRecord
  101.   IRecordInfo *pRecInfo
  102.   =
  103.   LONGLONG     llVal
  104.   =
  105.   LONG         lVal
  106.   =
  107.   BYTE         bVal
  108.   =
  109.   SHORT        iVal
  110.   =
  111.   FLOAT        fltVal
  112.   =
  113.   DOUBLE       dblVal
  114.   =
  115.   VARIANT_BOOL *pboolVal
  116.   =
  117.   SCODE        *pscode
  118.   =
  119.   CY           *pcyVal
  120.   '=
  121.  DATE         *pdate
  122.   =
  123.   BSTR         *pbstrVal
  124.   =
  125.   IUnknown     *punkVal
  126.   =
  127.   IDispatch    *pdispVal
  128.   =
  129.   SAFEARRAY    **pparray
  130.   =
  131.   'VARIANT      *pvarVal 'itr VARIANT
  132.  'VARIANT V
  133.  =
  134.   PVOID        byref
  135.   =
  136.   CHAR         cVal
  137.   =
  138.   USHORT       uiVal
  139.   =
  140.   ULONG        ulVal
  141.   =
  142.   ULONGLONG    ullVal
  143.   =
  144.   INT          intVal
  145.   =
  146.   UINT         uintVal
  147.   =
  148.   DECIMAL      *pdecVal
  149.   =
  150.   CHAR         *pcVal
  151.   =
  152.   USHORT       *puiVal
  153.   =
  154.   ULONG        *pulVal
  155.   =
  156.   ULONGLONG    *pullVal
  157.   =
  158.   INT          *pintVal
  159.   =
  160.   UINT         *puintVal
  161. end type
  162.  
  163. '#recordof VARIANT
  164.  
  165.  
  166. 'oleaut
  167. 'https://docs.microsoft.com/en-us/windows/desktop/api/oleauto/nf-oleauto-variantinit
  168.  

JRS

  • Guest
Re: COM
« Reply #8 on: October 13, 2020, 08:22:15 AM »
Here is a Rosetta code challenge using OLE automation.

https://rosettacode.org/wiki/OLE_Automation

JRS

  • Guest
Re: COM
« Reply #9 on: October 13, 2020, 12:26:38 PM »
Charles,

I'm going to try and convert your SAPI COM DLLC example to see if I can get it to load my VB6 ActiveX DLL form.

Here is the DLLC SAPI code. Can you show me how I can CREATE an instance of my COM DLL?

Code: OxygenBasic
  1. 'DECLARE MORE PROCEDURES FOR COM
  2.  
  3. CoInitialize     = dllproc(ole32,"CoInitialize (i)")
  4. CoUninitialize   = dllproc(ole32,"CoUninitialize (i)")
  5. CoCreateInstance = dllproc(ole32,"CoCreateInstance i=(t*ObjGuid ,i pUnkOuter,i context, t*IspGuid, i*Iface)" )
  6.  
  7. 'TRAP ERRORS
  8.  
  9. if dllerrc() then
  10.   print "DLLC logged errors: " & dllerrc() & "\n" & dllerrs()
  11.   goto ending
  12. end if
  13.  
  14.  
  15. 'COM SPEECH
  16.  
  17. VoiceObjGuid = dllguid("96749377-3391-11D2-9EE3-00C04F797396")
  18. ISpVoiceGuid = dllguid("6C44DF74-72B9-4992-A1EC-EF996E0422D4")
  19. Context      = 7
  20. pUnkOuter    = 0
  21. Voice        = 0
  22. Release      = dllmeth( 2,"Release i=()")
  23. Speak        = dllmeth(20,"Speak i=(z*pwcs,i flags,i pulstreamno)")
  24. WaitUntilDone= dllmeth(32,"WaitUntilDone i=(i)")
  25. print dllreco(speak)
  26. Text         = dllwstr("Hello Everyone!\0")
  27. hr=0
  28. dllcall(CoInitialize,0)
  29. hr=dllcall(CoCreateInstance, VoiceObjGuid, pUnkouter, Context, ISpVoiceGuid, Voice)
  30. if (hr=0) then
  31.   print "connected to voice\n\n"
  32.   print dllastr(Text) & "\n\n"
  33.   dllcobj(Voice,Speak,Text,0,0)
  34.   dllcobj(Voice,WaitUntilDone,0xFFFFFFFF)
  35.   dllcobj(Voice,Release)
  36. else
  37.   print "SAPI Error " & format("%x",hr) & "\n\n"
  38. end if
  39. dllcall(CoUninitialize)
  40.  

Here the ScriptBasic code to load the OCX form.

Code: Script BASIC
  1. obj = COM::CREATE(:SET, "MASLink.OCXForm")
  2.  

Attached is the VB6 ActiveX DLL you will need to register with regsvr32.

Note: The FlexGrid won't be sized or data populated with the raw load.
« Last Edit: October 13, 2020, 02:27:01 PM by John »

JRS

  • Guest
Re: COM
« Reply #10 on: October 13, 2020, 02:41:59 PM »
You will also need the updated common controls OCX the form uses. It doesn't need to be registered.

JRS

  • Guest
Re: COM
« Reply #11 on: October 13, 2020, 06:21:45 PM »
I forgot the enhanced FlexGrid is a separate OCX the form uses.  It doesn't need to be registered either.

This is the minimal statements needed to instantiate the form.

Code: Script BASIC
  1. IMPORT COM.sbi
  2.  
  3. obj = COM::CREATE(:SET, "MASLink.OCXForm")
  4.  
  5. COM::CBN obj, "ShowOCXForm"
  6.  
  7. COM::RELEASE obj
  8.  

Attached it what it should like if you get it working in O2.

 

Charles Pegge

  • Guest
Re: COM
« Reply #12 on: October 14, 2020, 05:26:04 AM »
A useful reference for creating COM clients and servers:

COM in plain C
Jeff Glatt
28 Mar 2006
https://www.codeproject.com/Articles/13601/COM-in-plain-C
parts
https://www.codeproject.com/search.aspx?q=COM+in+plain+C&x=0&y=0&sbo=kw

JRS

  • Guest
Re: COM
« Reply #13 on: October 14, 2020, 06:11:09 AM »
Outstanding resources Charles!

They really help understanding OLE automation.

Reminds me of your DLLC FFI interface.

Lua FFI
« Last Edit: October 14, 2020, 06:55:44 AM by John »

JRS

  • Guest
Re: COM
« Reply #14 on: October 18, 2020, 08:59:02 AM »
Charles,

Can you post an example of using Invoke() with your existing COM offering?