Author Topic: COM  (Read 6815 times)

0 Members and 1 Guest are viewing this topic.

Charles Pegge

  • Guest
Re: COM
« Reply #15 on: October 18, 2020, 09:39:23 PM »

JRS

  • Guest
Re: COM
« Reply #16 on: October 18, 2020, 10:03:40 PM »
There doesn"t look like there is much interest in COM here so I'll just have to be happy with what I have going with ScriptBasic.

I might try to take Dave's C COM extension module code and make it a generic DLL one could call from O2.
« Last Edit: October 19, 2020, 01:18:37 PM by John »

JRS

  • Guest
Re: COM
« Reply #17 on: October 23, 2020, 01:58:40 PM »
Charles,

Do you see anything in Jose's CallByName PowerBasic code that wouldn't port to O2?

Code: [Select]
' EXCEPINFO structure
' ********************************************************************************************
TYPE EXCEPINFO
   wCode AS WORD               ' An error code describing the error.
   wReserved AS WORD           ' Reserved
   bstrSource AS DWORD         ' Source of the exception.
   bstrDescription AS DWORD    ' Textual description of the error.
   bstrHelpFile AS DWORD       ' Help file path.
   dwHelpContext AS DWORD      ' Help context ID.
   pvReserved AS DWORD         ' Reserved.
   pfnDeferredFillIn AS DWORD  ' Pointer to function that fills in Help and description info.
   scode AS DWORD              ' An error code describing the error.
END TYPE
' ********************************************************************************************

' ********************************************************************************************
' Helper function to calculate the VTable address.
' ********************************************************************************************
FUNCTION TB_VTableAddress (BYVAL pthis AS DWORD, BYVAL dwOffset AS DWORD) AS DWORD
   LOCAL ppthis AS DWORD PTR
   LOCAL pvtbl AS DWORD PTR
   LOCAL ppmethod AS DWORD PTR
   ppthis = pthis
   pvtbl = @ppthis
   ppmethod = pvtbl + dwOffset
   FUNCTION = @ppmethod
END FUNCTION
' ********************************************************************************************

' ********************************************************************************************
' Maps a single member to a corresponding DispID, which can be used on subsequent calls to
' IDispatch_Invoke.
' Parameters:
' riid
'   Reserved for future use. Must be IID_NULL.
' strName
'   Name to be mapped.
' rgDispId
'   Retrieved DispID value.
' Return Value:
'   The return value obtained from the returned HRESULT is one of the following:
'   %S_OK                Success
'   %E_OUTOFMEMORY       Out of memory
'   %DISP_E_UNKNOWNNAME  One or more of the names were not known. The returned array of DISPIDs
'                        contains DISPID_UNKNOWN for each entry that corresponds to an unknown name.
'   %DISP_E_UNKNOWNLCID  The locale identifier (LCID) was not recognized.
' ********************************************************************************************
DECLARE FUNCTION Proto_IDispatch_GetIDOfName (BYVAL pthis AS DWORD, BYREF riid AS GUID, BYref rgszNames AS string, BYVAL cNames AS DWORD, BYVAL lcid AS DWORD, BYref rgdispid AS long) AS DWORD
FUNCTION IDispatch_GetIDOfName (BYVAL pthis AS DWORD, BYref strName AS string, BYref rgdispid AS long) AS DWORD
   LOCAL HRESULT AS DWORD
   LOCAL pmethod AS DWORD
   local riid as guid
   if pthis = 0 then exit function
   pmethod = TB_VTableAddress (pthis, 20)
   CALL DWORD pmethod USING Proto_IDispatch_GetIDOfName (pthis, riid, strName, 1, 0, rgdispid) TO HRESULT
   FUNCTION = HRESULT
END FUNCTION
' ********************************************************************************************

' ********************************************************************************************
' Provides access to properties and methods exposed by an object. The dispatch function DispInvoke
' provides a standard implementation of IDispatch_Invoke.
' Parameters:
' dispIdMember
'   Identifies the member. Use GetIDsOfNames or the object's documentation to obtain the dispatch identifier.
' riid
'    Reserved for future use. Must be IID_NULL.
' lcid
'   The locale context in which to interpret arguments. The lcid is used by the GetIDsOfNames
'   function, and is also passed to IDispatch_Invoke to allow the object to interpret its
'   arguments specific to a locale.
'   Applications that do not support multiple national languages can ignore this parameter.
' wFlags
'   Flags describing the context of the Invoke call, include:
'     %DISPATCH_METHOD
'       The member is invoked as a method. If a property has the same name, both this and the
'       %DISPATCH_PROPERTYGET flag may be set.
'     %DISPATCH_PROPERTYGET
'       The member is retrieved as a property or data member.
'     %DISPATCH_PROPERTYPUT
'       The member is changed as a property or data member.
'     %DISPATCH_PROPERTYPUTREF
'       The member is changed by a reference assignment, rather than a value assignment. This
'       flag is valid only when the property accepts a reference to an object.
' pDispParams
'   Pointer to a structure containing an array of arguments, an array of argument DISPIDs for
'   named arguments, and counts for the number of elements in the arrays.
' pVarResult
'   Pointer to the location where the result is to be stored, or NULL if the caller expects no
'   result. This argument is ignored if DISPATCH_PROPERTYPUT or DISPATCH_PROPERTYPUTREF is specified.
' pExcepInfo
'   Pointer to a structure that contains exception information. This structure should be filled
'   in if DISP_E_EXCEPTION is returned. Can be NULL.
' puArgErr
'   The index within rgvarg of the first argument that has an error. Arguments are stored in
'   pDispParams->rgvarg in reverse order, so the first argument is the one with the highest index
'   in the array. This parameter is returned only when the resulting return value is
'   %DISP_E_TYPEMISMATCH or %DISP_E_PARAMNOTFOUND. This argument can be set to null.
' Return Value:
'   The return value obtained from the returned HRESULT is one of the following:
'   %S_OK                     Success
'   %DISP_E_BADPARAMCOUNT     The number of elements provided to DISPPARAMS is different from the
'                             number of arguments accepted by the method or property.
'   %DISP_E_BADVARTYPE        One of the arguments in rgvarg is not a valid variant type.
'   %DISP_E_EXCEPTION         The application needs to raise an exception. In this case, the
'                             structure passed in pExcepInfo should be filled in.
'   %DISP_E_MEMBERNOTFOUND    The requested member does not exist, or the call to Invoke tried to
'                             set the value of a read-only property.
'   %DISP_E_NONAMEDARGS       This implementation of IDispatch does not support named arguments.
'   %DISP_E_OVERFLOW          One of the arguments in rgvarg could not be coerced to the specified type.
'   %DISP_E_PARAMNOTFOUND     One of the parameter DISPIDs does not correspond to a parameter on
'                             the method. In this case, puArgErr should be set to the first
'                             argument that contains the error.
'   %DISP_E_TYPEMISMATCH      One or more of the arguments could not be coerced. The index within
'                             rgvarg of the first parameter with the incorrect type is returned
'                             in the puArgErr parameter.
'   %DISP_E_UNKNOWNINTERFACE  The interface identifier passed in riid is not IID_NULL.
'   %DISP_E_UNKNOWNLCID       The member being invoked interprets string arguments according to
'                             the LCID, and the LCID is not recognized. If the LCID is not needed
'                             to interpret arguments, this error should not be returned.
'   %DISP_E_PARAMNOTOPTIONAL  A required parameter was omitted.
' ********************************************************************************************
FUNCTION IDispatch_Invoke (BYVAL pthis AS DWORD, BYVAL dispidMember AS LONG, BYREF riid AS GUID, _
BYVAL lcid AS DWORD, BYVAL wFlags AS WORD, BYREF pdispparams AS DISPPARAMS, BYREF pvarResult AS VARIANT, _
BYREF pexcepinfo AS EXCEPINFO, BYREF puArgErr AS DWORD) AS DWORD
   LOCAL HRESULT AS DWORD
   LOCAL pmethod AS DWORD
   if pthis = 0 then exit function
   pmethod = TB_VTableAddress (pthis, 24)
   CALL DWORD pmethod USING IDispatch_Invoke (pthis, dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr) TO HRESULT
   FUNCTION = HRESULT
END FUNCTION
' ********************************************************************************************

' ********************************************************************************************
' CallByName
' ********************************************************************************************
function TB_CallByName ( _
    BYVAL pthis AS DWORD, _                                    ' *IDispatch
    BYVAL vNameOrId AS VARIANT, _                              ' Name or identifier
    byval callType as long, _                                  ' Call type
    byref vParams() as VARIANT, _                              ' Array of variants
    byref vResult as variant, _                                ' Variant result
    byref pex as EXCEPINFO _                                   ' EXCEPINFO
    ) EXPORT AS LONG                                           ' Error code

    dim dw_puArgErr AS DWORD, DISPID_PROPERTYPUT AS LONG, IID_NULL AS GUID
    dim vArgs(0) AS VARIANT, udt_DispParams AS DISPPARAMS
    dim strName as string, DispID as long, nParams as long, i as long, idx as long

    ' Check for null pointer
    if pthis = 0 then function = -1 : exit function

    ' Get the DispID
    if variantvt(vNameOrId) = %VT_BSTR then
       strName = ucode$(variant$(vNameOrId))
       IDispatch_GetIDOfName pthis, strName, DispID
    else
       DispID = variant#(vNameOrId)
    end if

    ' Copy the array in reversed order
    if varptr(vParams()) then
       nParams = ubound(vParams) - lbound (vParams) + 1
       if nParams > 0 then
          redim vArgs(nParams - 1)
          idx = nParams - 1
          for i = Lbound(vParams) to Ubound(vParams)
             if variantvt(vParams(i)) = %VT_EMPTY then
                vArgs(idx) = error %DISP_E_PARAMNOTFOUND
             else
                vArgs(idx) = vParams(i)
             end if
             DECR idx
             if idx < 0 then exit for
          next
       end if
   end if

   if CallType = 4 or CallType = 8 then  ' %DISPATCH_PROPERTYPUT and %DISPATCH_PROPERTYPUTREF
      DISPID_PROPERTYPUT = -3
      udt_DispParams.CountNamed = 1
      udt_DispParams.NamedDispId = VARPTR(DISPID_PROPERTYPUT)
   end if

   udt_DispParams.CountArgs = nParams
   if nParams > 0 then udt_DispParams.VariantArgs = varptr(vArgs(0))

   function = IDispatch_Invoke(pthis, DispID, IID_NULL, 0, CallType, udt_DispParams, vResult, pex, dw_puArgErr)

END function
' ********************************************************************************************

Example Use
Code: [Select]
#COMPILE EXE
#DIM ALL
#DEBUG ERROR ON
#INCLUDE "WIN32API.INC"
#INCLUDE "CallByName.INC"

%adOpenKeyset     = &H00000001
%adLockOptimistic = &H00000003
%adCmdText        = &H00000001

' ********************************************************************************************
' Main
' ********************************************************************************************
function pbmain

   local oCon as dispatch
   local oRec as dispatch
   local hr as dword
   local pex as EXCEPINFO
   local vResult as VARIANT
   local bstrlen as long
   dim vParams(0) as variant
   
   ' Creates a connection instance
   set oCon = new dispatch in "ADODB.Connection"
   if isfalse isobject(oCon) then goto Terminate
   
   redim vParams(3)  ' Four parameters (0:3) - Empty variants are considered as optional parameters
   vParams(0) = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=f:\ffado\biblio.mdb"  ' <-- change as needed
   hr = TB_CallByName(objptr(oCon), "Open", %DISPATCH_METHOD, vParams(), BYVAL %NULL, BYVAL %NULL)

   ' Creates a recordset instance
   set oRec = new dispatch in "ADODB.Recordset"
   if isfalse isobject(oRec) then goto Terminate
   
   ' Opens the recordset
   redim vParams(4)  ' Five parameters (0:4)
   vParams(0) = "SELECT TOP 20 * FROM Authors ORDER BY Author"
   set vParams(1) = oCon  ' This is a dispatch variable, so we have to assign it using SET
   vParams(2) = %adOpenKeyset
   vParams(3) = %adLockOptimistic
   vParams(4) = %adCmdText
   hr = TB_CallByName(objptr(oRec), "Open", %DISPATCH_METHOD, vParams(), BYVAL %NULL, BYVAL %NULL)

   do
      hr = TB_CallByName(objptr(oRec), "Eof", %DISPATCH_PROPERTYGET, byval %NULL, vResult, BYVAL %NULL)
      if variant#(vResult) then exit do
      redim vParams(0)  ' One parameter
      vParams(0) = "Author"
      hr = TB_CallByName(objptr(oRec), "Collect", %DISPATCH_PROPERTYGET, vParams(), vResult, BYVAL %NULL)
      print variant$(vResult)
      ' Fetch the next row
      hr = TB_CallByName(objptr(oRec), "MoveNext", %DISPATCH_METHOD, byval %NULL, BYVAL %NULL, BYVAL %NULL)
   loop
   
   
Terminate:

   ' Close the reordset
   hr = TB_CallByName(objptr(oRec), "Close", %DISPATCH_METHOD, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)
   ' Close the connection
   hr = TB_CallByName(objptr(oCon), "Close", %DISPATCH_METHOD, BYVAL %NULL, BYVAL %NULL, BYVAL %NULL)

   ' Cleanup
   set oRec = nothing
   set oCon = nothing

   waitkey$

end function
' ********************************************************************************************

JRS

  • Guest
Re: COM
« Reply #18 on: October 24, 2020, 01:09:02 PM »
Brian,

If you can get CallByName working in O2 I will buy a copy of your BASIC.

Charles Pegge

  • Guest
Re: COM
« Reply #19 on: October 24, 2020, 03:22:02 PM »
Hi John,

What does this do? where is ADODB?

Code: [Select]
  set oCon = new dispatch in "ADODB.Connection"

JRS

  • Guest
Re: COM
« Reply #20 on: October 24, 2020, 08:06:55 PM »
ADO is a Windows DB layer COM object. I would use Excel for your testing.

https://www.allbasic.info/forum/index.php?topic=414.msg4520#msg4520

Charles Pegge

  • Guest
Re: COM
« Reply #21 on: October 25, 2020, 11:01:24 AM »
How is VBNEW implemented. I see no GUIDs, so are you using ATL to locate the interfaces?

JRS

  • Guest
Re: COM
« Reply #22 on: October 25, 2020, 11:09:42 AM »
I would use Dave Zimmer's C CallByName example. He even used the same SAPI example you did in DLLC. (see code in comments)

The SB COM extension module let's you use a GUID or it's registered name.

Here is Dave's original SB COM repository. It doesn't have the improvements Mike made.

Dave's SB COM
« Last Edit: October 25, 2020, 11:23:06 AM by John »

Charles Pegge

  • Guest
Re: COM
« Reply #23 on: October 25, 2020, 06:51:00 PM »
Thanks John,  this is what I was after:

Code: [Select]
if(myCopy[0] == '{'){
hr = CLSIDFromString( wStr , &clsid); //its a string CLSID directly
  }else{
hr = CLSIDFromProgID( wStr , &clsid); //its a progid
  }

JRS

  • Guest
Re: COM
« Reply #24 on: October 26, 2020, 12:22:15 AM »
Great!

The C code I posted above has the improvements Mike made that are not in Dave's repo.

FYI: ADO is the OLE automation version of ODBC.

Note: regsvr32 needs to run in an administrator console when registering controls.
« Last Edit: October 26, 2020, 12:58:30 PM by John »

JRS

  • Guest
Re: COM
« Reply #25 on: November 02, 2020, 07:09:44 AM »
Hi Charles,

Are you still looking at a CallByName library for O2?

This is my last attempt at trying to get something going here on the forum.

It's getting towards the end of the year and the forums I host are lacking any activity. Please give me a reason to keep spending money so others have a resouce to go to.

Charles Pegge

  • Guest
Re: COM
« Reply #26 on: November 02, 2020, 02:30:25 PM »
Hi John,

Unfortunately, I seem to have hit a brick wall with the iDispatch interface. I have tried it on the SAPI voice and also the WebBrowser control accessed via ATL. But it appears defunct in both cases, at least in my Windows10 environment.

But I was able to establish low-level COM control on Aurel's ATL webBrowser Control demo - attaching GoForward/GoBack/GoHome to the Window Buttons.

JRS

  • Guest
Re: COM
« Reply #27 on: November 02, 2020, 02:59:57 PM »
Thanks for giving it try.

If I find some time I will try to convert the SB COM extension to something generic O2 can use.

Posting your code and getting a few more eyes looking at it might discover the problem.
« Last Edit: November 02, 2020, 08:36:33 PM by John »

JRS

  • Guest
Re: COM
« Reply #28 on: November 03, 2020, 08:35:09 AM »
Charles,

Writing a BASIC compiler, a MD5 function in ScriptBasic and the magic of DLLC makes it hard to imagine you getting stuck on anything.

Charles Pegge

  • Guest
Re: COM
« Reply #29 on: November 03, 2020, 12:05:21 PM »
I think the problem is Windows10.