` Printed Icetips Article

Icetips Article



COM: Creating a COM object
2004-01-16 -- Jim Kane
 
Newsgroups: sv.clarion.documentation


   As I remember it the time I was shocked to see something work is when I
wrote the original piece of assembler to call an interface for the 1st time.
I had a lot of trouble finding info down to the register level so I had to
guess a little.  when it finally worked, I was quite amazed.
I was also amazed the 1st time I created a complex com object that could
work in a ASP page because I had trouble figuring out how to interact with
the ASP Response object.  The problem was at the time I didnt understand the
stack conventions for passing variants by value but finally thru a lot of
debugger work and disassembly figured it out - all 16 bytes of the variant
went on the stack.  Shortly after that the com object was fully functional.
Below are the details for creating an enumerator com object - very simple.

jim kane

Creating a com object that implements IUnknown and not IDispatch  unlike the
one I used in an ASP page on the other hand is very easy.
the pseudo code is

addref  self.count+=1;return SELF.Count
release
  SELF.count-=1;
  if count=0 then
  lpSelf=address(self)
  RefToSelf&=(lpSelf)
  dispose(RefToSelf) !self destruct your class
  end
  return SELF.Count
QueryInterface

Use the api memcmp or use a loop and compare a byte at a time but find a way
to compare two IIDs and see if they are byte for byte the same
All my stdcom classes have a compare uuid method in them than can be used
plus there is one below. Then once you can compare two 16 byte groups:

if iid_passedIn = IID:Iunknow or iid_passedin=IID:IEnumString then
lpVoid=address(SELF.IEnumString)
returnvalue=0
else
  lpvoid=0
  returnvalue=E_noInterface
end
return returnvalue

Some time ago I created a IDragSource com object for oledrag and drop.  To
to that I had to create an IDataobject class and type (Com object).
Fortunately I did not injur myself .
That IDataObject class in turn had to implement IEnumFormatetc which is just
one example of the IEnumXXXX type of enumerators - doesnt matter much what
you are enumerating. Strings and FormatETC groups - the code is the same.
Here is the entire code - there may be some debugging code left. The code
that went into production is at work.

!this code is in IDataObject and creates the enumerator and returns it.
!The only trick is the item being enumed (FormatETC groups in this case) 
!is suppose to be in com allocated memory so rather than using
! new() to create the memory to hold the data being enumerated I call
!cotaskmemalloc().  It works just like new()
! I think you could get away with using new since your object implements
!release but I followed the 'rules'.


DataobjClType.IDataObjectType.EnumFormatEtc       Procedure(long dwDirection, |
                                                           *long lpIEnumFormatEtc)!,long
!in,out

I       long,auto
recs    long,auto
lpdata  long,auto
ptr     long,auto
cbbytes long,auto
Iunk    &Iunktype
bufsize long,auto
  code
  if dwDirection<>1 then return e_notImpl.
  !enum avail formats
  SELF.IenumCl&=New IEnumClType
  if SELF.IEnumCl&=NULL then return e_outofmemory.
  !make the data
  recs=Records(SELF.FormatQ)
  cbbytes=size(formatetctype)
  bufsize=recs*cbbytes
  !allocate com memory
  lpdata=cotaskmemalloc(bufsize)
  if ~lpData then
    dispose(SELF.IEnumCl)
    Return e_outOfMemory
  end
  !copy from the queue into com memory
  ptr=lpdata
  loop i=1 to recs
    get(SELF.FormatQ,I)
    Memcpy(ptr,Address(SELF.FormatQ.cfFormat),cbbytes)
    ptr+=cbbytes
  end
  !give the enum class the info it needs to come to life.
  SELF.IenumCl.init(lpdata,cbbytes, recs,Address(iid:IEnumFormatetc))
  lpIEnumFormatEtc=Address(SELF.IEnumCl.IEnumXXXtype)
  !message('enumformatetc done')
  return S_OK


!this is the actual com object enumerator - you could use it to create an
!IEnumString if you wanted:

!----------------IEnumformatxxx
------------------------------------------------------------
!member variable
!refcount long
!lpdata long !ptr to the data
!cbdata long !size of one element
!maxidx long !1 based max element
!idx    long !current position
!myiid  group(guidtype) !memcpy in the iid for this object
!00000103-0000-0000-C000-000000000046


IEnumClType.init        procedure(long plpdata, |
                                  long pcbdata, |
                                  long pmaxidx, |
                                  long plpIID)

  code
  SELF.Refcount=1
  SELF.lpdata=plpData
  SELF.cbData=pcbdata
  SELF.maxidx=pMaxidx
  if pMaxidx=0 then
    SELF.idx=0
  else
    SELF.idx=1
  end
  memcpy(address(SELF.myiid),plpIID,16)
  Return

IEnumClType.IsUIDEqual  procedure(long lpuid1, |
                                  long lpUid2) !,byte !returns 1 if the uids are equal
else 0

  code
  if ~lpuid1 or ~lpuid2 then return 0.
  return choose(Memcmp(lpuid1,lpuid2,16)=0,1,0)



IEnumClType.IEnumxxxtype.QueryInterface      PROCEDURE (long  iid_Requested, |
                                                       *LONG ppvobj)!,long

hr long,auto
  Code
  if SELF.IsUidEqual(address(IID:IUnknown),iid_Requested) or |
     SELF.IsUidEqual(Address(SELF.MyIID),iid_Requested) then
    SELF.Ienumxxxtype.AddRef()
    ppvobj=Address(SELF.IEnumxxxType)
    hr = S_OK
  else
    hr=E_noInterface
    clear(ppvobj)
  end
  Return hr


IEnumClType.IEnumxxxtype.AddRef              PROCEDURE ()!,Long,PROC

  code
  SELF.Refcount+=1
  return SELF.Refcount


IEnumClType.IEnumxxxtype.Release             PROCEDURE ()!,Long,PROC

dummyself &IEnumClType
  code
  SELF.Refcount-=1
  if SELF.Refcount<=0 then
    if SELF.lpdata then cotaskMemFree(SELF.lpData);clear(SELF.lpdata).
    dummyself&=(Address(SELF))
    dispose(dummyself)
    return 0
  end
  return SELF.Refcount


IEnumClType.IEnumxxxtype.Nextxxx       Procedure(long cEltIn, |
                                                 long lpDataOut, |
                                                 long lpCeltfetchedOut ) !,long

hr long,auto
formatEtc &formatetctype
dummylong long
  code
  !handle the no data case
  if ~SELF.lpData or ~SELF.idx or ~SELF.MaxIdx or ~SELF.cbData or ~lpdataout
then
    dummylong=0
    if lpCeltFetchedout
      Memcpy(lpceltfetchedout,address(dummylong),4)
    end
    Return S_False
  end

  !COMPUTE  CELTFETCHEDOUT
  hr=S_OK
  if cEltIn+SELF.idx>SELF.maxidx then
    cEltIn=SELF.maxidx-SELF.Idx+1
    if cEltin<0 then cEltin=0.
    hr=S_false
  end

  !return the celtfetchecout value unless the pointer is null
  if lpCeltFetchedout
    dummylong=celtin
    Memcpy(lpceltfetchedout,address(dummylong),4)
  end

  !compute the pointer
  if celtin then
    Memcpy(lpDataOut, SELF.lpData + SELF.CbData*(SELF.idx-1) , celtin*SELF.CbData)
  end

  !MOVE THE IDX WITH BOUND CHECKING
  SELF.idx = SELF.idx+cEltin
  if SELF.idx<1 or SELF.idx>SELF.Maxidx then SELF.idx=1.  !reset to 1 if out of bounds
  Return hr


IEnumClType.IEnumxxxtype.skipxxx       procedure(long celtin)!,long

hr long,auto
  code
  !if no data
  if ~SELF.lpData or ~SELF.idx or ~SELF.MaxIdx then Return s_false.
  !COMPUTE  CELTFETCHEDOUT
  hr=S_OK
  if cEltIn+SELF.idx>SELF.maxidx then
    cEltIn=SELF.maxidx-cEltIn
    if cEltin<0 then cEltin=0.
    hr=S_false
  end

  !MOVE THE IDX WITH BOUND CHECKING
  SELF.idx = SELF.idx+cEltin
  if SELF.idx<1 or SELF.idx>SELF.Maxidx then SELF.idx=1.
  Return hr


Ienumcltype.Ienumxxxtype.resetxxx      procedure()!,long

hr long,auto
  code
  hr=S_OK
  SELF.idx=1
  return hr


Ienumcltype.Ienumxxxtype.clonexxx      procedure(*long lpIenumxxxOut)!,long

lpdata  long,auto
IEnumCl &IEnumClType
  code
  clear(lpIEnumxxxout)

  !make a new enumerator object
  IenumCl&=New IEnumClType
  if IEnumCl&=NULL then return e_outofmemory.

  !make the data
  lpdata=cotaskmemalloc(SELF.MaxIDX*SELF.CbData)
  if ~lpData then
    dispose(IEnumCl)
    Return e_outOfMemory
  end
  !copy the data
  memcpy(lpdata,SELF.lpData,SELF.MaxIDX*SELF.CbData)

  !init the new enumerator
  IenumCl.init(lpdata,SELF.cbData, SELF.maxidx,Address(iid:IEnumFormatetc))

  !set the internal pointer to the same pos as the parent class
  IEnumCl.idx=SELF.idx

  !return the pointer for the new enumerator clone
  lpIenumxxxOut=Address(IEnumCl.IEnumXXXtype)
  return S_OK



Printed May 4, 2024, 11:44 pm
This article has been viewed/printed 35134 times.
Google search has resulted in 114 hits on this article since January 25, 2004.