` Creating a COM object (Jim Kane) - Icetips Article
Icetips - Templates, Tools & Utilities for Clarion Developers

Templates, Tools and Utilities
for Clarion Developers

Icetips Article

Back to article list   Search Articles     Add Comment     Printer friendly     Direct link  

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


Today is November 21, 2024, 3:43 am
This article has been viewed 35405 times.
Google search has resulted in 114 hits on this article since January 25, 2004.



Back to article list   Search Articles   Add Comment   Printer friendly

Login

User Name:

Password: