示例#1
0
loTrid loCacheUpdate(loService *se, unsigned count, loTagValue taglist[], FILETIME *timestamp)
{
 unsigned ii;
 loTagValue *ts;
#if 0 != LO_FILL_TIMESTAMP
 FILETIME ft;
#endif
// UL_DEBUG((LOGID, "loCacheUpdate(%p %u %p)", se, count, taglist));
 if (!loSERVICE_OK(se)) return 0;//EBADF; /*ERROR:EARG*/
 if (!timestamp && (0 >= count || !taglist)) return 0;
#if 0 != LO_FILL_TIMESTAMP
#if 1 == LO_FILL_TIMESTAMP
 se->driver.ldCurrentTime(&se->cactx, &ft);
#else
 if (timestamp) ft = *timestamp;
 else memset(&ft, 0, sizeof(ft));
#endif
#endif

 if (!(ts = loCacheLock(se)))
   {
    UL_INFO((LOGID, "loCacheUpdate() lock failed"));
    return 0;//-1; /*ERROR:out-of-service*/
   }

 for(ii = 0; ii < count; ii++, taglist++)
   {
    loTagId ti;

    if (0 == (ti = taglist->tvTi)) continue;
    else if (ti < 1 || ti > se->lastused/*tag_count*/)
      {
       /*rv = -1;*//*ERROR:BAD ti*/
       UL_INFO((LOGID, "loCacheUpdate(BAD ti:%u)", ii));
       continue;
      }
    else
      {
       HRESULT hr;
       loTagValue *tv = &ts[ti];
       tv->tvState = taglist->tvState;
#if 0 != LO_FILL_TIMESTAMP
       if (!IsFILETIME(tv->tvState.tsTime)) tv->tvState.tsTime = ft;
#endif
/*     hr = (V_ISBYREF(&taglist->tvValue)?
                       VariantCopyInd(&tv->tvValue, &taglist->tvValue):
                       VariantCopy(&tv->tvValue, &taglist->tvValue)); */
       hr = VariantCopy(&tv->tvValue, &taglist->tvValue);
       if (S_OK != hr)
         {
          LO_E_BADTYPE_QUAL(hr, tv->tvState.tsError,
                            tv->tvState.tsQuality);
          /*rv = -1;*//*ERROR:BAD VALUE*/
          UL_WARNING((LOGID, "%!l loCacheUpdate(VariantCopy())", hr));
         }
       tv->tvTi = ti;
      }
   }
 return loCacheUnlock(se, timestamp);
/* return rv; */
}
示例#2
0
lw_thrrettype loUpdatePipe(void *vse)
{
 loService *se = (loService *)vse;
 if (!loIS_VALID(se))
   {
    UL_ERROR((LOGID, "loUpdatePipe() prematurely terminated"));
    lw_RETURN;
   }
 UL_TRACE((LOGID, "loUpdatePipe() started..."));
 loThrControl_accept(&se->update_pipe);
 lw_mutex_lock(&se->update_pipe.lk);
 for(;; lw_conds_wait(&se->update_pipe.cond, &se->update_pipe.lk))
   if (0 != se->update_pipe.tstate)
     {
      if (0 > se->update_pipe.tstate) break; /* terminate thread */
      lw_mutex_unlock(&se->update_pipe.lk);
      lw_rw_wrlock(&se->lkPrim);
      lw_mutex_lock(&se->update_pipe.lk);
      if (0 > se->update_pipe.tstate)
        { lw_rw_unlock(&se->lkPrim); break; }/* terminate thread */
{       /* do actual update here */
 unsigned ii = se->lastused;
 loTagEntry *te = se->tags;
 loTagValue *ts = se->secondary;
 loTrid prim_changed = se->sec_trid;

 while(ii--)
   {
    te++;
    ts++;
    if (ts->tvTi)
      {
       if (ts->tvTi == loCH_TIMESTAMP)
         te->prim.tsTime = ts->tvState.tsTime;
       else
         {
          te->primChanged = prim_changed;
          te->prim = ts->tvState;
#if LO_KEEP_OLD_CACHE
          { HRESULT hr;
          if (S_OK != (hr = VariantCopy(&te->primValue, &ts->tvValue)))
            {
             LO_E_BADTYPE_QUAL(hr, te->prim.tsError, te->prim.tsQuality);
             UL_WARNING((LOGID, "%!l loUpdatePipe(VariantCopy())", hr));
          } }
#else
          VariantClear(&te->primValue);
          te->primValue = ts->tvValue;
          VARIANTINIT(&ts->tvValue);
#endif
         }
       ts->tvTi = 0;
      }
   }
}
      se->prim_trid = se->sec_trid;
#if 0 != LO_EV_TIMESTAMP
      if (se->ts_prim != se->ts_sec) 
        {
         freeX(se->ts_prim);
         se->ts_prim = se->ts_sec;
        }
      memcpy(se->ts_prim, 
             se->ts_prim + se->ts_size, 
             se->ts_size * sizeof(FILETIME));
#endif
      lw_rw_unlock(&se->lkPrim);
      se->update_pipe.tstate = 0;
      lw_condb_broadcast(&se->lkTridWait);
     }
 loThrControl_finish(&se->update_pipe);
 lw_mutex_unlock(&se->update_pipe.lk);
 UL_NOTICE((LOGID, "loUpdatePipe() finished"));

 lw_RETURN;
}
示例#3
0
int loInternalServiceDestroy(loService *se)
{
    int rv0, rv;
    unilog *log;

    UL_TRACE((LOGID, "loInternalServiceDestroy(%p)...", se));

    if (!loIS_VALID(se))
    {
        UL_ERROR((LOGID, "loInternalServiceDestroy(%p) illegal pointer", se));
        return EBADF;
    }
    se->iam = 0;
    log = (unilog*)se->log;
    se->log = 0;
    rv = 0;

    if ((se->initphase & ifLKSEC) &&
            (rv0 = loThrControl_destroy(&se->update_pipe)) && !rv) rv = rv0;

    if ((se->initphase & ifLKMGMT) &&
            (rv0 = lw_rwlock_destroy(&se->lkMgmt)) && !rv) rv = rv0;

    if ((se->initphase & ifLKPRIM) &&
            (rv0 = lw_rwlock_destroy(&se->lkPrim)) && !rv) rv = rv0;

    if ((se->initphase & ifLKDR) &&
            (rv0 = lw_mutex_destroy(&se->lkDr)) && !rv) rv = rv0;

    if ((se->initphase & ifLKLIST) &&
            (rv0 = lw_mutex_destroy(&se->lkList)) && !rv) rv = rv0;

    if ((se->initphase & ifTRWAIT) &&
            (rv0 = lw_condb_destroy(&se->lkTridWait)) && !rv) rv = rv0;

    se->initphase = 0;

    /* cleanup the CACHE & ATTRIB */
    if (se->secondary) loTagValue_clear(se->secondary, se->tag_count);
    if (se->tags)      loTagEntry_clear(se->tags, se->tag_count);
    se->tag_count = 0;
    lo_proplist_clear(se);

    se->ts_size = 0;
    if (se->ts_prim != se->ts_sec && se->ts_sec) freeX(se->ts_sec);
    se->ts_sec = 0;
    if (se->ts_prim) freeX(se->ts_prim);
    se->ts_prim = 0;

    freeX(se);

    if (rv) UL_WARNING((LOGID, "%!e loDelete() finished", rv));
    else UL_TRACE((LOGID, "loDelete() finished = %u", rv));
    {
#if 0 != LO_USE_MALLOCX
//    extern long mallocX_count;
        UL_WARNING((LOGID, "loDelete()::mallocX = %ld xobjref = %ld",
                    mallocX_count, lo_X_objcount));
#endif
        mallocX_trap();
    }

    if (log) unilog_Delete(log);

    return rv;
}
示例#4
0
int loServiceCreate(loService **result, const loDriver *drv, unsigned tagcount)
{
    int rv = 0;
    size_t size;
    loService *se;

    if (!result) return EINVAL;
    *result = 0;
    if (!drv || 0 >= tagcount || 0 >= ++tagcount) return EINVAL;

    UL_DEBUG((LOGID, "structs: tE=%u tA=%u tD=%u tV=%u tS=%u tP=%u",
              sizeof(loTagEntry), sizeof(loTagAttrib), sizeof(loTagDetail),
              sizeof(loTagValue), sizeof(loTagState), sizeof(loTagPair)));

    size = sizeof(loService) +
           (sizeof(loTagEntry) + sizeof(loTagValue) + sizeof(lo_hash)) * tagcount;
    UL_TRACE((LOGID, "loCreate(%u) requested size = %u", tagcount - 1, size));

    se = (loService*)mallocX(size);
    if (!se)
    {
        UL_ERROR((LOGID, "loCreate(%u) requested size = %ud FAILED", tagcount, size));
        return ENOMEM;
    }
    se->iam = se;
    se->servlist = 0;
    se->serv_key = 0;
    se->shutdown = 0;

    se->initphase = 0;
    se->tag_count = 0;
    se->firstfree = 1; /* first entry is not used */
    se->lastused  = 0;
    se->lastnamed = 0;

    se->proplist = 0;
    se->proplist_count = 0;

    se->driver = *drv;

    se->cactx.ca_se = se;
    se->cactx.ca_se_arg = se->driver.ldDriverArg;
    se->cactx.ca_cli = 0;
    se->cactx.ca_cli_arg = 0;

    se->ts_prim = se->ts_sec = 0;
    se->ts_size = 0;

    se->log = 0;
#if 0 <= USE_LOG
    if (!lolog)
    {
        lolog = (unilog*)(se->log = INIT_LOG());
        UL_INFO((LOGID, "UNILOG initialization missed..."));
        UL_TRACE((LOGID, "loCreate(%u) requested size = %u", tagcount - 1, size));
    }
#endif

    se->ts_prim = se->ts_sec = (FILETIME*)mallocX(2 * sizeof(FILETIME));
    if (!se->ts_prim)
    {
        rv = ENOMEM;
        UL_ERROR((LOGID, "ts_prim init() FAILED"));
        goto Fail;
    }
    memset(se->ts_prim, 0, 2 * sizeof(FILETIME));
    se->ts_size = 1;

    if (rv = lo_proplist_init(se))
    {
        UL_ERROR((LOGID, "lo_proplist_init() FAILED"));
        goto Fail;
    }

    lo_setup_clock();
    if (se->driver.ldRefreshRate < 1)
    {
        se->driver.ldRefreshRate = lo_default_timegran(
                                       se->driver.ldRefreshRate_min < 1 ?
                                       &se->driver.ldRefreshRate_min : 0);
        if (se->driver.ldRefreshRate/*_min*/ < 1)
            se->driver.ldRefreshRate/*_min*/ = 16;
    }

    if (se->driver.ldRefreshRate_min < se->driver.ldRefreshRate)
        se->driver.ldRefreshRate_min = se->driver.ldRefreshRate;

    if (se->driver.ldQueueMax < 1) se->driver.ldQueueMax = 4; /* DEFAULT */

    if ((se->driver.ldFlags & loDf_EE_SFALSE) == loDf_EE_SFALSE)
        se->driver.ldFlags &= ~loDf_EE_SFALSE;

    se->wstrcmp = (se->driver.ldFlags & loDF_IGNCASE)? _wcsicmp: wcscmp;
    se->wstrncmp = (se->driver.ldFlags & loDF_IGNCASE)? _wcsnicmp: wcsncmp;
    se->wstrhash = (se->driver.ldFlags & loDF_IGNCASE)? lo_wcsihash: lo_wcshash;
#if 0
    se->wstrnhash = (se->driver.ldFlags & loDF_IGNCASE)? lo_wcsnihash: lo_wcsnhash;
#endif

    if (!se->driver.ldCurrentTime) se->driver.ldCurrentTime = ld_current_time;

    if (!se->driver.ldBranchSep ||
            0 >= mbtowc(&se->branch_sep, &se->driver.ldBranchSep, 1)) se->branch_sep = 0;
    UL_TRACE((LOGID, "Branch Separator = \\x%02X <%lc>\\x%02X",
              se->driver.ldBranchSep, se->branch_sep, se->branch_sep));

    se->tags = (loTagEntry*)(&se[1]);
    loTagEntry_init(se->tags, tagcount);
    se->secondary = (loTagValue*)(&se->tags[tagcount]);
    loTagValue_init(se->secondary, tagcount);
    se->tag_count = tagcount;
    se->name_hash = (lo_hash*)(&se->secondary[tagcount]);
    memset(se->name_hash, 0, sizeof(lo_hash) * tagcount);
    /* we assume VT_EMPTY === 0 */
    se->sec_trid = se->prim_trid = 0;
    /* se->prim_changed = 0; */

    se->tags[0].attr.taDetail = (loTagDetail*)mallocX(sizeof(loTagDetail));
    if (!se->tags[0].attr.taDetail)
    {
        UL_ERROR((LOGID, "loCreate(%u) taDetail FAILED", tagcount));
        rv = ENOMEM;
        goto Fail;
    }
    loTagDetail_init(se->tags[0].attr.taDetail);
    se->tags[0].attr.taFlags = loTt_DETAILS | loTF_EMPTY;
    se->tags[0].attr.taDetail->tdName[0] = 0;
    se->tags[0].attr.taRangecent = 0.0;

    if (rv = lw_rwlock_init(&se->lkMgmt, 0))
    {
        UL_ERROR((LOGID, "loCreate()::lkMgmt FAILED"));
        goto Fail;
    }
    se->initphase |= ifLKMGMT;

    if (rv = lw_rwlock_init(&se->lkPrim, 0))
    {
        UL_ERROR((LOGID, "loCreate()::lkPrim FAILED"));
        goto Fail;
    }
    se->initphase |= ifLKPRIM;

    if (rv = lw_mutex_init(&se->lkList, 0))
    {
        UL_ERROR((LOGID, "loCreate()::lkList FAILED"));
        goto Fail;
    }
    se->initphase |= ifLKLIST;

    if (rv = lw_mutex_init(&se->lkDr, 0))
    {
        UL_ERROR((LOGID, "loCreate()::lkDr FAILED"));
        goto Fail;
    }
    se->initphase |= ifLKDR;

    if (rv = lw_condb_init(&se->lkTridWait, 0))
    {
        UL_ERROR((LOGID, "loCreate()::lkTridWait FAILED"));
        goto Fail;
    }
    se->initphase |= ifTRWAIT;

    if (rv = loThrControl_init(&se->update_pipe))
    {
        UL_ERROR((LOGID, "loCreate()::loThr_init FAILED"));
        goto Fail;
    }
    se->initphase |= ifLKSEC;

    if (rv = loThrControl_start(&se->update_pipe, 0, loUpdatePipe, se))
    {
        UL_ERROR((LOGID, "loCreate()::loThr_start FAILED"));
        goto Fail;
    }

    se->cform_dataonly = RegisterClipboardFormat("OPCSTMFORMATDATA");
    se->cform_datatime = RegisterClipboardFormat("OPCSTMFORMATDATATIME");
    se->cform_writecompl = RegisterClipboardFormat("OPCSTMFORMATWRITECOMPLETE");

    *result = se;
    return 0;
Fail:
    UL_WARNING((LOGID, "%!e loCreate(%u) failed", rv, tagcount));
    loServiceDestroy(se);
    return rv;
}
int LightOPCServer::send_callback(LightOPCGroup *grp, 
                                  unsigned group_key, loUpdList *upl, int advmask)
{
 int tr = 0;
 IOPCDataCallback *c_databack = 0;
 IAdviseSink *c_datatime = 0,
             *c_dataonly = 0,
             *c_writecomp = 0;
 unsigned cform_dtime, cform_donly, cform_wrcompl;
 OPCHANDLE client_handle;

// UL_DEBUG((LOGID, "send_notify(){..."));
 if (!upl->used)
   {
    if (!upl->trqid) return 0;
    if (S_OK == upl->master_err) upl->master_err = S_FALSE;
   }

 if (FAILED(upl->master_err)) upl->master_qual = S_FALSE;

 if (!grp)
   {
    lock_read();
    if (0 == (grp = by_handle_g(group_key)))
      {
       unlock();
       UL_INFO((LOGID, "send_callback(%x) FAILED: No such group"));
       return 0;
      }
   }
#if LO_USE_BOTHMODEL && 0
 else group_key = grp->ServerHandle;
#endif
 client_handle = grp->ClientHandle;

/* There is only client_sched() thread allowed to change connection info under read_lock ! */
 if ((advmask & loRQ_CONN_DATABACK) && grp->conn_databack)
   loGET_IFACE(c_databack, grp->conn_databack, IOPCDataCallback, grp->initphase);

 if (advmask & (loRQ_CONN_DATA_1 | loRQ_CONN_WRITECOMPL))
   {
    if ((advmask & loRQ_CONN_DATATIME) && grp->conn_datatime)
      {
       cform_dtime = se->cform_datatime;
       loGET_IFACE(c_datatime, grp->conn_datatime, IAdviseSink, grp->initphase);
/* ?? advise_present &= ~loRQ_CONN_DATATIME */
      }

    if ((advmask & loRQ_CONN_DATAONLY) && grp->conn_dataonly)
      {
       cform_donly = se->cform_dataonly;
       loGET_IFACE(c_dataonly, grp->conn_dataonly, IAdviseSink, grp->initphase);
/* ?? advise_present &= ~loRQ_CONN_DATAONLY */
      }

    if ((advmask & loRQ_CONN_WRITECOMPL) && grp->conn_writecompl)
      {
       cform_wrcompl = se->cform_writecompl;
       loGET_IFACE(c_writecomp, grp->conn_writecompl, IAdviseSink, grp->initphase);
/* ?? advise_present &= ~loRQ_CONN_WRITECOMPL */
      }
   }

 unlock();

// UL_DEBUG((LOGID, "send_notify() #3"));
 if (c_databack)
   {
    HRESULT hr = E_INVALIDARG;

    switch(advmask & loRQ_OPER_MASK)
      {
    case loRQ_OP_REFRESH:
       hr = c_databack->OnDataChange(upl->trqid, client_handle,
            upl->master_err, upl->master_qual, upl->used, upl->opchandle,
            upl->variant, upl->quality, upl->timestamp, upl->errors);
       break;
    case loRQ_OP_READ:
       hr = c_databack->OnReadComplete(upl->transaction_id, client_handle,
            upl->master_err, upl->master_qual, upl->used, upl->opchandle,
            upl->variant, upl->quality, upl->timestamp, upl->errors);
       break;
    case loRQ_OP_WRITE:
       hr = c_databack->OnWriteComplete(upl->transaction_id, client_handle,
            upl->master_err, upl->used, upl->opchandle, upl->errors);
       break;
    case loRQ_OP_CANCEL:
       hr = c_databack->OnCancelComplete(upl->transaction_id, client_handle);
       break;
      }
    c_databack->Release();
    if (S_OK == hr) 
      {
       tr++;
       UL_DEBUG((LOGID, "OnDataChange2(0x%X) Ok", advmask));
      }
    else
      {
       UL_WARNING((LOGID, "%!l OnDataChange2(0x%X) FAILED", hr, advmask));
      }
//    UL_WARNING((LOGID, "OnDataChange() 2 Ok"));
   }

 if (advmask & loRQ_CONN_DATA_1)
   {
    HGLOBAL gmem = 0;
    unsigned datasize = 0;

    if (c_datatime)
      {
//       UL_DEBUG((LOGID, "OnDataChange1() with time"));
       if (0 <= lo_OnDataChange1(upl, client_handle, cform_dtime,
                                1, c_datatime, &gmem, &datasize)) tr++;
       c_datatime->Release();
      }
    if (c_dataonly)
      {
//       UL_DEBUG((LOGID, "OnDataChange1() without time"));
       if (0 <= lo_OnDataChange1(upl, client_handle, cform_donly,
                                0, c_dataonly, &gmem, &datasize)) tr++;
       c_dataonly->Release();
      }

    if (gmem && (// && (!GlobalSize(gmem) ||*/ GlobalFree(gmem)))
      //(GlobalFlags(gmem) & (GMEM_INVALID_HANDLE | GMEM_DISCARDED)) || 
        GlobalFree(gmem) ) )
      {
       UL_WARNING((LOGID, "%!L GlobalFree(%X) FAILED flags=0x%X", 
                           gmem, GlobalFlags(gmem)));       
      }
   }
 else if (c_writecomp)
   {
//    UL_DEBUG((LOGID, "OnWriteComplete1()"));
    if (0 <= lo_OnWriteComplete1(upl, client_handle, cform_wrcompl,
                                 c_writecomp, 0)) tr++;
    c_writecomp->Release();
   }

// UL_DEBUG((LOGID, "send_datachange() finished}"));
 return tr;
}
示例#6
0
int lo_OnDataChange1(loUpdList *upl, OPCHANDLE hClient, unsigned cform,
                     int with_time, IAdviseSink *ias, HGLOBAL *gmem, unsigned *hint_dsize)
{
 HGLOBAL fakegmem = 0;
 unsigned hdrsize, totalsize, ihdr_size, datasize;
 unsigned ii, itcount = upl->used;
 loUpdList lup = *upl;

 ihdr_size = with_time? sizeof(OPCITEMHEADER1): sizeof(OPCITEMHEADER2);
 hdrsize = sizeof(OPCGROUPHEADER) + itcount * ihdr_size;
 datasize = 0;

 if (hint_dsize && *hint_dsize) datasize = *hint_dsize;
 else
   {
    for(ii = itcount; ii--;)
      {
       datasize += loVariantSize(&lup.variant[ii]);
      }
    if (hint_dsize) *hint_dsize = datasize;
   }

 totalsize = hdrsize + datasize;

 if (!gmem) gmem = &fakegmem;
 if (!gmem_realloc(gmem, totalsize))
   {
    itcount = 0;
    hdrsize = sizeof(OPCGROUPHEADER);
    if (!gmem_realloc(gmem, hdrsize))
      {
       UL_ERROR((LOGID, "%!L Trasaction %X ABORTED due gmem_alloc(%u)",
                 lup.trqid, totalsize));
       return -1;
      }
    UL_WARNING((LOGID, "%!L Trasaction %X Failed due gmem_alloc(%u)",
                 lup.trqid, totalsize));
    totalsize = hdrsize;
    lup.master_err = E_OUTOFMEMORY;
   }

{
 char *glob, *glob0, *ihdr;
 OPCGROUPHEADER *gh;
 FILETIME faketime, *timest = &faketime;
 unsigned timest_inc = 0;

 glob = glob0 = (char*)GlobalLock(*gmem);
 gh = (OPCGROUPHEADER*)glob;
 ihdr = (char*)&gh[1];
 glob += hdrsize;

 if (with_time)
   timest_inc = sizeof(OPCITEMHEADER1),
   timest = &((OPCITEMHEADER1*)ihdr)->ftTimeStampItem;

 gh->dwSize = totalsize;
 gh->dwItemCount = itcount;
 gh->hClientGroup = hClient;
 gh->dwTransactionID = lup.trqid;

 for(ii = 0; ii < itcount; ii++)
   {
    ((OPCITEMHEADER1*)ihdr)->wReserved = 0;
    ((OPCITEMHEADER1*)ihdr)->dwValueOffset = glob - glob0;
    ((OPCITEMHEADER1*)ihdr)->hClient  = lup.opchandle[ii];
    ((OPCITEMHEADER1*)ihdr)->wQuality = lup.quality[ii];
    loVariantPack((void**)&glob, &lup.variant[ii]);
    *timest = lup.timestamp[ii];
    timest = (FILETIME*)(((char*)timest) + timest_inc);
    ihdr += ihdr_size;
   }

 gh->hrStatus = lup.master_err != S_OK?
                lup.master_err: lup.master_qual;

 if ((unsigned)(glob - glob0) != totalsize)
   {
    UL_ERROR((LOGID, "Transaction:%X datasize mismatch %u %u",
                      lup.trqid, glob - glob0, totalsize));
   }
 GlobalUnlock(*gmem);
}
{
 STGMEDIUM stgm;
 FORMATETC form;

 form.cfFormat = cform;
 form.dwAspect = DVASPECT_CONTENT;
 form.ptd = NULL;
 form.tymed = TYMED_HGLOBAL;
 form.lindex = -1;
 stgm.tymed = TYMED_HGLOBAL;
 stgm.STGM_hGlobal = *gmem;
 stgm.pUnkForRelease = DUMMY_UNKNOWN;

 UL_DEBUG((LOGID, "OnDataChange1:%s-time(%u)...%p{ %X",
                  with_time? "with": "without", 
                  itcount, ias, *gmem));
 ias->OnDataChange(&form, &stgm);
 UL_DEBUG((LOGID, "OnDataChange()...}"));
}
 gmem_free(&fakegmem);

 return FAILED(lup.master_err)? -1: 0;
}
示例#7
0
int lo_OnWriteComplete1(loUpdList *upl, OPCHANDLE hClient, unsigned cform,
                        IAdviseSink *ias, HGLOBAL *gmem)
{
 HGLOBAL fakegmem = 0;
 unsigned datasize;
 unsigned ii, itcount = upl->used;
 loUpdList lup = *upl;

 datasize = sizeof(OPCGROUPHEADERWRITE) + itcount * sizeof(OPCITEMHEADERWRITE);

 if (!gmem) gmem = &fakegmem;
 if (!gmem_realloc(gmem, datasize))
   {
    itcount = 0;
    datasize = sizeof(OPCGROUPHEADERWRITE);
    if (!gmem_realloc(gmem, datasize))
      {
       UL_ERROR((LOGID, "%!L Trasaction %X ABORTED due gmem_alloc(%u)",
                 lup.trqid, datasize));
       return -1;
      }
    UL_WARNING((LOGID, "%!L Trasaction %X Failed due gmem_alloc(%u)",
                 lup.trqid, datasize));
    lup.master_err = E_OUTOFMEMORY;
   }

{
 OPCGROUPHEADERWRITE *gh;
 OPCITEMHEADERWRITE *ihdr;

 gh = (OPCGROUPHEADERWRITE*)GlobalLock(*gmem);
 ihdr = (OPCITEMHEADERWRITE*)&gh[1];

 gh->dwItemCount = itcount;
 gh->hClientGroup = hClient;
 gh->dwTransactionID = lup.trqid;
 gh->hrStatus = lup.master_err;

 for(ii = 0; ii < itcount; ii++, ihdr++)
   {
    ihdr->hClient = lup.opchandle[ii];
    ihdr->dwError = lup.errors[ii];
// UL_DEBUG((LOGID, "OnWriteComplete(%u : %X/%X)", itcount, ihdr->dwError, gh->hrStatus));
   }

 if ((unsigned)((char*)ihdr - (char*)gh) != datasize)
   {
    UL_ERROR((LOGID, "Transaction:%X datasize mismatch %u %u",
                      lup.trqid, (char*)ihdr - (char*)gh, datasize));
   }
 UL_DEBUG((LOGID, "OnWriteComplete(%u : %X/%X)", itcount, gh->hrStatus, lup.master_err));
 GlobalUnlock(*gmem);
}
{
 STGMEDIUM stgm;
 FORMATETC form;

 form.cfFormat = cform;
 form.dwAspect = DVASPECT_CONTENT;
 form.ptd = NULL;
 form.tymed = TYMED_HGLOBAL;
 form.lindex = -1;
 stgm.tymed = TYMED_HGLOBAL;
 stgm.STGM_hGlobal = *gmem;
 stgm.pUnkForRelease = DUMMY_UNKNOWN;

 ias->OnDataChange(&form, &stgm);
 UL_DEBUG((LOGID, "OnWriteComplete()...}"));
}
 gmem_free(&fakegmem);

 return FAILED(lup.master_err)? -1: 0;
}