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 November 23, 2024, 4:50 am This article has been viewed/printed 35408 times.
Google search
has resulted in 114 hits on this article since January 25, 2004.
|