function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
dukeduke 

Calling ISForceSession::Create from C++

Hi,

I'm using the SForceOfficeToolkit from within an ActiveX control, which is written in C++.  I am trying to create Activity History entries (i.e., Tasks) relating to contacts or leads.  Everything works up until the point I call ISForceSession.Create.  This call throws a Dispatch Exception.  The task objects I passed to this call are encapsulated in a VARIANT that holds a SAFEARRAY of IDispatch objects.  Said differently, the vt field is set to VT_ARRAY | VT_DISPATCH.

Is there anyone out there who has successfully called the SForceOfficeToolkit from C++ and, in particular, who has created some sort of object via the Create method?

Regards,

Duke

SuperfellSuperfell
That probably should be an array of variants, with each variant containing a dispatch pointer. (i.e. VT_ARRAY | VT_VARIANT for the safearray, and VT_DISPATCH for each variant in the array)
dukeduke

Simon,

Thanks for your reply!  I tried that and it didn't work.  Perhaps something else is wrong, I'm not sure.  Are you certain the disp interface objects have to be encapsulated within VARIANTs?  I've been running down my fair share of "rabbit holes" on this one.  Do you know of any C++ samples?

Duke

qmanqman

Hi Duke,

I just grabbed some code and quickly edited out the parts specific to my application. I have an SObject of the proper entity (pCurSObject) before I start down this code.

Bill

  _variant_t myFields = pCurSObject->Fields;

  // Get a pointer to the elements of the array.
  VARIANT *pFields;
  HRESULT hr = SafeArrayAccessData(myFields.parray, (void **)&pFields);

  // Loop thru all the columns of the accessor
  ULONG nColumns = GetColumnCount();
  for (ULONG i=0; i  {
   // i is the accessor index, starting with column 0
   ULONG iOrdinal = acc.GetOrdinal(i
);

   // get index of this field into SOAP msg fields
   ULONG iSOAPIndex = selectListObject.objFields[iOrdinal-1].myFieldsIndex;
   IField2Ptr pField(pFields[iSOAPIndex]);

   if (colStatus == DBSTATUS_S_ISNULL)
   {
    // yes, so set field value to null
    _variant_t emptyValue;
    emptyValue.vt = VT_EMPTY;

    pField->PutValue(emptyValue);
    
    // all done with this field, so continue to next one
    continue;
   }

   // Map buffer value into SObject
   switch (type) {
   case DBTYPE_STR:
    {
    // Convert STR to WSTR because sforce doesn't work with STR
    CA2W pValue((char *)pColData);
    _variant_t wstrValue(pValue);
    pField->PutValue(wstrValue);
    }
    break;
   case DBTYPE_WSTR:
    {
    wchar_t *pValue = (wchar_t *)pColData;
    _variant_t wstrValue(pValue);
    pField->PutValue(wstrValue);
    }
    break;
   case DBTYPE_CY:
    {
    CURRENCY *pValue = (CURRENCY *)pColData;
    _variant_t curValue(*pValue);
     pField->PutValue(curValue);
    }
    break;
   case DBTYPE_I4:
    {
    long *pValue = (long *)pColData;
    _variant_t lValue(*pValue);
    pField->PutValue(lValue);
    }
    break;
   case DBTYPE_UI2:
    {
    // booleans are handled as shorts so they can be passed in remote queries
    USHORT *pValue = (USHORT *)pColData;
    _variant_t sValue(*pValue);
    pField->PutValue(sValue);
    }
    break;
   case DBTYPE_R8:
    {
    double *pValue = (double *)pColData;
    _variant_t dValue(*pValue);
    pField->PutValue(dValue);
    }
    break;
   case DBTYPE_DATE:
    {
    DATE *pValue = (DATE *)pColData;
    _variant_t dValue(*pValue);
    pField->PutValue(dValue);
    }
    break;
   case DBTYPE_BOOL:
    {
    short *pValue = (short *)pColData;
    _variant_t bValue(*pValue,VT_BOOL);
    pField->PutValue(bValue);
    }
    break;
   }
  }
  SafeArrayUnaccessData(myFields.parray);

dukeduke

Hey Bill,

Thanks for the post!  That helps.  Can I ask you two questions though?

1) From your code I can see how you pull information out of a SAFEARRAY returned from sfdc.  Do you have any code that demonstrates how you construct a SAFEARRAY to give to sfdc?  I'm trying to figure out if the SAFEARRAY should contain VARIANTS which inturn encapsulate VT_DISPATCH interfaces, or should the SAFEARRAY contain VT_DISPATCH objects that are not wrapped as VARIANTS.

2) I see that you use IField2 at one point.  Can you tell me the difference between IField2 and IField, or ISObject2 and ISObject?  Perhaps this demonstrates some COM ignorance on my part.

Thanks Duke

dukeduke

Hey Bill,

I have a 3rd question for ya:

I'm currently wrapping the IDispatch interface as follows - soTask is an ISObject.

_variant_t vDisp;

vDisp.vt = VT_DISPATCH;

vDIsp.pdispVal = soTask.m_lpDispatch;

long aLong[1];

aLong[0] = 0;

hr = SafeArrayPutElement(pSA, aLong, &vDisp);

I then wrap the SAFEARRAY in another VARIANT and pass it to ISForceSession::Create().  That blows up.  I know the soTask object is ok becuase it works fine for many previous operations.  So here's my question: Is it valid to merely use the m_lpDispatch member variable when wrapping the Dispatch interface?  Is there another way to do this from an ISObject?

Duke

qmanqman

Hi Duke,

1. I can't help with building them from scratch. I was easier for me to get a prebuilt object using the Create call and then use the toolkit api to put values into that object.  IMHO, this isolates your code from changes the toolkit authors may make to the internal COM object structure.

2. Always use the -2 versions.  The -2 versions inherit all the earlier interfaces. That way, both the original methods and the -2 methods are available.

Bill

Message Edited by qman on 06-22-2005 05:42 AM

dukeduke

Hey Bill,

Thanks!  I'm going to convert my objects to the "2" versions to see if that helps/matters.  I think I'll eventually go with a "late binding" approach; as you say, it does insolate the implementation.  But for now, I just want/need to get this working.

Thanks again.

Duke

dukeduke

I finally got this to work, so I thought I would reveal the lessons I learned.

1) The members of the SAFEARRAY can and should be of the type VT_DISPATCH.  You shouldn't wrap them as VARIANTS.  The SAFEARRAY itself must be wrapped in a single VARIANT.

2) I used the ISObject2 interface as opposed to the ISObject interface, although either will work.

3) It is easier to call ISObject2::Create as opposed to the ISForceSession::Create.  Since I am only creating one task at a time, this is the better choice.

Many thanks to Bill and Simon for taking the time to respond.

Regards,

Duke