Discussion:
Help please passing OleVariant array of BSTR and Pinteger
(too old to reply)
Dennis Reichel
2004-06-30 16:15:59 UTC
Permalink
This post was previously posted in:
[borland.public.delphi.com.activex.using]

I'd appreciate some help with an aspect of interfacing to the Act!
contact management software's ACTOLE.DLL. Included with the SDK is
documentation including code examples of most operations using
VB and C++. There is no mention of Delphi, but I've managed
to get everything to work in D7 as expected, except for a few
functions having arrays as parameters.

GetDataEx is a method of a com object in actole.dll/ACTOLE_TLB
It requires an array of integers, representing field tags, and an array
of strings, to contain the contents of those fields referenced. There
is a similar function: SetDataEx for writing to the Act! contact database.

I believe that Widestring is equivalent to BSTR and to varOleString
and that these types are Unicode OLE-compatible.

I'm not at all sure about the best integer type to use, but I think
I've tried them all, including unsigned types unsuccessfully.

Here's an example of what I've been trying to make work:

procedure TActContact.GetDataExTesting;
var
vFields,vValues: OleVariant;
aBase,aHigh: integer;
begin
aBase:=1; // have tried 0 and 1
aHigh:=aBase+1;
vFields:=VarArrayCreate([aBase,aHigh],varInteger); // at least 16 bits for
max value
vFields[aBase]:=CF_Name;
vFields[aHigh]:=CF_Company;
vValues:=VarArrayCreate([aBase,aHigh],varOleStr);
vValues[aBase]:='';
vValues[aHigh]:='';
fIContact.GetDataEx(vFields,vValues);
if fIContact.Error then
ShowMessageFmt('Error %s Getting Data: %s ',
[ActForm.LastErrorName(fIContact.LastError),Company]);
end;

Invariably:
-1999: Result:= 'Status_InvalidParam'; // invalid parameter

From the ActDevelopers Reference.pdf

[Syntax] object.GetDataEx iFieldArray, szValueArray

[Parameters]
iFieldArray Returns an array of short integers (or pointers to
short integers in VISUAL C++) that represent the field IDs of fields for
which to get the data contained in
the fields. The Field ID array must be an array of integers - variant arrays
do not work.

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^???
szValueArray Returns an array of strings (or BSTRs in VISUAL C++) for the
data that
is returned for the specified fields. The same number of elements must be
specified for
the iFieldArray and the szValueArray parameters.

Note: In Visual C++, use VARIANT.pparray instead of VARIANT.parray for
pointers to
elements in the array.

A sample wrapper for GetDataEx C++ shows the interface description:

void IContact::GetDataEx(const VARIANT& vFieldArray, const VARIANT&
vValueArray)
{
static BYTE parms[] =
VTS_VARIANT VTS_VARIANT;
InvokeHelper(0xe0, DISPATCH_METHOD, VT_EMPTY, NULL, parms,
&vFieldArray, &vValueArray);
}

From the D7 TLB:

procedure GetDataEx(vFieldArray: OleVariant; vValueArray: OleVariant);
dispid 224;

procedure TContact.GetDataEx(vFieldArray: OleVariant; vValueArray:
OleVariant);
begin
DefaultInterface.GetDataEx(vFieldArray, vValueArray);
end;

I've underlined the passage: The Field ID array must be an array of
integers - variant arrays do not work.

If this holds true in Delphi, as in C++, then for the life of me, how does
one create an array of integers that is type compatible with OleVariant?
Or perhaps more accurately, an OleVariant type compatible pointer to
an array of PInteger...

Here is VB sample code:
'This example demonstrates getting data from multiple fields and sending
'it to a file.
Dim NoteIdArray(10) As Integer
Dim OutputArray(10) As String
Dim objDatabase As Object
Dim objNoteHistory As Object
Dim count As Double
Dim LastRecord As Long
Dim FileNum
NoteIdArray(0) = NHF_ContactId'Field ID constant
NoteIdArray(1) = NHF_UserTime'Include Actfield.bas
NoteIdArray(2) = NHF_Type
NoteIdArray(3) = NHF_Text
Set objDatabase = CreateObject("ACTOLE.DATABASE")
objDatabase.Open dbName
If objDatabase.IsMultiUser Then
objDatabase.ValidateUser "Chris Huffman", ""
End If
If objDatabase.IsOpen = False Then
MsgBox "Failed to open database"
End If
Set objNoteHistory = objDatabase.NoteHistory
LastRecord = objNoteHistory.recordcount
count = 1
'Get a free file number
FileNum = FreeFile
'Open GetNoteHistory.TXT for append
Open "GetNoteHistory.txt" For Append As FileNum
objNoteHistory.MoveFirst
Do While (count <= LastRecord)
objNoteHistory.GetDataEx NoteIdArray, OutputArray
Write #FileNum, OutputArray(0), OutputArray(1), OutputArray(2),
OutputArray(3)
count = count + 1
objNoteHistory.MoveNext
If objNoteHistory.IsEOF Then
Exit Do
End If
Loop

---

I am instantiating the ole objects as variants using CreateOleObject if that
has any
bearing on how these parameters would be handled.

That looks REALLY easy - what's the secret to doing it in Delphi? Any
suggestions,
background information, or even wild guesses will be appreciated.

Thanks in advance, Dennis Reichel
***@reichel.net
John Carlyle-Clarke
2004-07-01 08:37:43 UTC
Permalink
Any suggestions,
background information, or even wild guesses will be appreciated.
As a wild guess, I think you need to use safearrays. I believe an
OleVariant can containt a safearray. Safearrays are an API supported
array type. VB uses them for its arrays, which makes me think this is
the case - if you pass a VB array to a COM object, a safearray is what
you get.

This post contains some sample code for doing a safearray of strings -
should be easy enough to adapt it for integers too.

I could be totally wrong here, as I've never used Act!, but I have
passed VB arrays to and from a Delphi COM object and that is what I
used.

http://groups.google.com/groups?hl=en&lr=lang_en&ie=UTF-8
&selm=Xns94BB70CAFDF92johncceuroplacercouk%40192.168.1.69&rnum=2

Oh, the Delphi equivalent of the VB integer is "short", so I'd guess
that is the type you need to use.

I'm not 100% sure about the part about "pointers to short integers".
I don't know if a safearray of short is an array of short or an array
of pointers to short. Maybe someone else can shed some light on that?

Well, that's my guess anyway :)
Dennis Reichel
2004-07-06 03:48:38 UTC
Permalink
Thanks for your reply. I pursued safearrays, without getting anywhere.

Here is what finally worked for the Act contact getdataex and setdataex
procedures:

procedure TActContact.GetDataExTesting;
var
vFieldArray,vValueArray:OleVariant;
begin
vFieldArray:=VarArrayCreate([0,1],varSmallInt);
vFieldArray[0]:=CF_Name;
vFieldArray[1]:=CF_Company;
vValueArray:=VarArrayCreate([0,1],varOleStr);
fIContact.GetDataEx(VarArrayRef(vFieldArray),VarArrayRef(vValueArray));
if fIContact.Error then
ShowMessageFmt('Error %s Getting Data: %s
',[ActForm.LastErrorName(fIContact.LastError),Company])
else
ShowMessageFmt('Contact: %s Company:
%s',[vValueArray[0],vValueArray[1]]);
end;

A URL that pointed me in the right direction:
http://mysite.verizon.net/~vze2vjds/delphi/circle.htm

Best Regards,
Dennis Reichel
Post by John Carlyle-Clarke
As a wild guess, I think you need to use safearrays. I believe an
OleVariant can containt a safearray. Safearrays are an API supported
array type. VB uses them for its arrays, which makes me think this is
the case - if you pass a VB array to a COM object, a safearray is what
you get.
John Carlyle-Clarke
2004-07-06 09:39:43 UTC
Permalink
Post by Dennis Reichel
Thanks for your reply. I pursued safearrays, without getting
anywhere.
Here is what finally worked for the Act contact getdataex and
[snip code]

Very interesting! Thanks for following up, and glad you got it
working.

I've always been sure there is some mapping between the variant arrays
and the safearrays - that maybe they are even totally equivalent - but
I've never quite managed to get my head around it, but that code and
site really helps. I need to play around a bit more.

Loading...