COM: Create a VT_ARRAY with VT_BSTR values - com

I'm a COM newbie and I think what I have is correct, but the runtime doesn't like it. Any help is much appreciated.
I need to invoke a COM function that takes in a single dimensional array of BSTRs. Specifically, the documentation says the parameter must be:
Function: AddFiles ( [in] VARIANT * filePaths )
filePaths The single-dimensioned array of full paths to each file or
folder. filePaths can be of type
VT_ARRAY|VT_VARIANT, where each entry
is a VT_BSTR, or VT_ARRAY|VT_BSTR.
I have a vector<wstring> myPaths of paths which I want to pass into the function that takes the parameter above. Here's the code I wrote. Calling AddFiles on myComObject results in an AV (myComObject is not null, and I can invoke other methods on it):
...
VARIANT filePaths;
VariantInit( &filePaths );
filePaths.vt = VT_ARRAY|VT_VARIANT;
filePaths.parray = SafeArrayCreateVector( VT_BSTR, 0, (unsigned int) myPaths.size() );
long i = 0;
for( vector<wstring>::iterator it = myPaths.begin();
it != myPaths.end();
it++, i++ )
{
BSTR myPath= SysAllocString(it->c_str());
SafeArrayPutElement( filePaths.parray, &i, myPath);
}
myComObject->AddFiles( &filePaths );
...
The COM object isn't my code and I can't debug into it, but I suspect I'm not creating that array properly - based on the requirement of the AddFiles function and the code I have, anyone have ideas about what I might be doing wrong?

If myComObject->AddFiles can only deal with VT_ARRAY|VT_VARIANT, the following should work too.
VARIANT myPath;
VariantInit(&myPath);
myPath.vt = VT_BSTR;
myPath.bstrVal = SysAllocString(it->c_str());
SafeArrayPutElement(filePaths.parray, &i, &myPath);

Don't you want:
filePaths.vt = VT_ARRAY|VT_BSTR;
Since you're creating a SafeArray of BSTRs?

Related

Three different ways to instantiate Arrays in AssemblyScript

I'm writing a smart contract and want to use Arrays to manipulate data, but looking at the AssemblyScript docs, I'm not sure the best way to proceed.
It seems fine to me to just use:
let testData:string[] = []
but when I consulted the assemblyscript docs, there are three recommended ways to create an Array:
// The Array constructor implicitly sets `.length = 10`, leading to an array of
// ten times `null` not matching the value type `string`. So, this will error:
var arr = new Array<string>(10);
// arr.length == 10 -> ERROR
// To account for this, the .create method has been introduced that initializes
// the backing capacity normally but leaves `.length = 0`. So, this will work:
var arr = Array.create<string>(10);
// arr.length == 0 -> OK
// When pushing to the latter array or subsequently inserting elements into it,
// .length will automatically grow just like one would expect, with the backing
// buffer already properly sized (no resize will occur). So, this is fine:
for (let i = 0; i < 10; ++i) arr[i] = "notnull";
// arr.length == 10 -> OK
When would I want to use one type of instantiation over another? Why wouldn't I just always use the version I presented in the beginning?
Nothing wrong with the array literal approach. It is basically equivalent to
let testData = new Array<string>();
However, sometimes you know what the length of the array should be and in such cases, preallocating the memory using Array.create is more efficient.
UPDATE
With this PR Array.create deprecated and should not be used anymore.
OLD ANSWER
let testData:string[] = []
semantically the same as
let testData = new Array<string>()
AssemblyScript doesn't support preallocated sparse arrays (arrays with holes) for reference elements which not explicitly declared as nullable like:
let data = new Array<string>(10);
data[9] = 1; // will be compile error
Instead you could use:
let data = new Array<string | null>(10);
assert(data.length == 10); // ok
assert(data[0] === null); // ok
or Array.create but in this case your length will be zero. Array.create is actually just reserve capacity for backing buffer.
let data = Array.create<string>(10);
assert(data.length == 0); // true
For plain (non-reference) types you could use usual way without care about nullabe or creating array via Array.create:
let data = new Array<i32>(10);
assert(data.length == 10); // ok
assert(data[0] == 0); // ok

Merge two SAFEARRAY's - SafeArrayPutElement memory access violation

I am getting some memory access violation while execute the following code:
UINT cDims = 1;
SAFEARRAYBOUND rgsabound[1];
long lLbound = 0;
long lUbound = 0;
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = pList1->rgsabound[0].cElements + pList2->rgsabound[0].cElements;
SAFEARRAY* mergeResult = SafeArrayCreate(VT_DISPATCH, cDims, reinterpret_cast<SAFEARRAYBOUND*>(rgsabound));
// Obtain bounds information of the SAFEARRAY.
SafeArrayGetLBound(pList2, 1, &lLbound);
SafeArrayGetUBound(pList2, 1, &lUbound);
long lDimSize = lUbound - lLbound + 1;
GoldMineConstantContactCOM::IBounceActivityPtr ptrActivity;
SafeArrayCopy(pList1, &mergeResult);
rgsabound[0].lLbound = 0;
rgsabound[0].cElements = pList1->rgsabound[0].cElements + pList2->rgsabound[0].cElements;
SafeArrayRedim(mergeResult, rgsabound);
for (int i = 0; i < lDimSize; i++)
{
long rgIndices[1];
rgIndices[0] = i;
HRESULT hRes2 = SafeArrayGetElement(pList2, rgIndices, &ptrActivity);
rgIndices[0] = rgIndices[0] + pList1->rgsabound[0].cElements;
HRESULT hRes = SafeArrayPutElement(mergeResult, rgIndices, (void*)&ptrActivity);
}
return mergeResult;
The message I got is: Unhandled exception at 0x774115de : 0xC0000005: Access violation reading location 0x00000004.
Any help will be very helpful!
Thanks in advance
Regards,
Fabian
The & in SafeArrayCopy(pList1, &mergeResult); raised suspicions that the documentation confirmed: The function does not copy from the source array to your allocated one, it overwrites the pointer (leaking it) with a new array of the same dimension. However, your call to SafeArrayRedim appears to take care of (part of) the problem.
Also, you take care of retrieving the lower bound of pList2, but you don't use it during the actual copy.
Then, I'm not sure the smart pointer is used exactly right. I think maybe you should put its declaration inside the loop.
Finally, I think I've found the real culprit: The documentation of SafeArrayPutElement say:
The variant types VT_DISPATCH, VT_UNKNOWN, and VT_BSTR are pointers, and do not require another level of indirection.
Which means you should remove the & in (void*)&ptrActivity.

how to assign pointer in a managed array of pointers c++/cli?

I am new to managed code and i need to pass array of pointers to different structures to windows form using C++/CLI , but it didn`t work !
My problem is in the managed array, how can i correctly access its elements .
The code sequence :
array<void*> ^ ptr;//here ptr value is undefined , type array<void*> ^
ptr = gcnew array<void*> (2);// length 0x2 , 0x0 and 0x1 values are undefined of type void
class1::struct1 structObj1;
class2::struct2 structObj2;
ptr[0] = &structObj1;// value is empty of type void!!
ptr[1] = &structObj2;//value is empty of type void!!
When i watched ptr , i found the above comments.
Notice that repeating code but using unmanaged array works probably
void* ptr[2];//here ptr value is undefined , type void*[]
class1::struct1 structObj1;
class2::struct2 structObj2;
ptr[0] = &structObj1;// value is address1 of type void*
ptr[1] = &structObj2;//value is address2 of type void*
Can anyone see where is the problem??
Do I need to use unmanaged array then convert to managed? If yes, how can I do it ??
Passing unmanaged pointers in a managed array may be valid C++/CLI, but it's definitely not the ideal way to do things. Do consider creating a custom managed class (ref class in C++/CLI) to hold the structures, instead of passing around pointers.
For this, I'm assuming that struct1 and struct2 are unmanged structs. This answer only applies if that is the case.
Your existing code works for me. Here's my version, with some debugging added in.
public struct struct1 { int foo; };
public struct struct2 { float bar; };
int main(array<System::String ^> ^args)
{
array<void*> ^ ptr;
ptr = gcnew array<void*> (2);
for(int i = 0; i < ptr->Length; i++)
Debug::WriteLine("ptr[{0}] = {1:X8}", i, reinterpret_cast<int>(ptr[i]));
struct1 structObj1;
struct2 structObj2;
ptr[0] = &structObj1;
ptr[1] = &structObj2;
for(int i = 0; i < ptr->Length; i++)
Debug::WriteLine("ptr[{0}] = {1:X8}", i, reinterpret_cast<int>(ptr[i]));
struct1* pointerToStructObj1 = reinterpret_cast<struct1*>(ptr[0]);
structObj1.foo = 4;
Debug::WriteLine("pointerToStructObj1->foo = {0}", pointerToStructObj1->foo);
}
Output:
ptr[0] = 00000000
ptr[1] = 00000000
ptr[0] = 0013F390
ptr[1] = 0013F394
pointerToStructObj1->foo = 4
Edit
To use Debug::WriteLine, add using namespace System::Diagnostics.
The debugger doesn't know how to display the contents of a void*, so it just displays blank. It does display a null pointer differently, though: null shows up as <undefined value>, non-null shows up as just blank.
My philosophy on C++/CLI is: If you're going to write managed code, write managed code. Consider replacing your vector with a managed List. If you still need unmanaged objects, I strongly urge you to consider writing a managed class with properly typed pointers, rather than a void* array.
To implement such a class, create whatever fields you need, just be sure that they're pointers, not direct. (vector<foo>* instead of vector<foo>.) Create the objects with new in the constructor, and delete them in the destructor (which is called on Dispose) & finalizer.

Accessing a SafeArray of Variants with JNI

I have a VB6 ActiveX DLL with functions that return a Variant. The Variant contains an array of node Variants, each of which contains a string Name and two data arrays (string and double). I am attempting to return this to a Java program as a jobject through JNI.
I can access the outer array of nodes by calling the appropriate VB function and storing the Variant result as a SAFEARRAY. It can access the dimension and get lower and upper bounds. However, I cannot access each node through SafeArrayGetElement() or SafeArrayAccessData(). I always get an Invalid Argument exception.
1) Can I pass or cast the SAFEARRAY (or VARIANT) directly to a jobject without iterating through the nodes in C++?
2) Am I using the wrong parameters to get the SAFEARRAY data? Does the size of the access pointer (var) need to be allocated beforehand?
SAFEARRAY* outarr = t->VBFunction(&bstrparam).GetVARIANT().parray;
//Returns correct dimension (1)
printf("JNI GetNodes_States: Got array, dimension %d\n", outarr->cDims);
//Returns correct bounds
LONG lBound, rBound;
SafeArrayGetLBound(outarr, 1, &lBound);
SafeArrayGetUBound(outarr, 1, &rBound);
printf("JNI GetNodes_States: Bounds [%d, %d]\n", lBound, rBound);
//Returns Invalid Argument error (hresult=0x80070057)
//Gets first element
LONG* indexArray = new LONG[outarr->cDims];
for(unsigned short i=0; i<outarr->cDims; ++i)
indexArray[i] = 0;
_variant_t var;
hresult = SafeArrayGetElement(outarr, indexArray, (void*)&var);
if (SUCCEEDED(hresult)){
printf( "JNI GetNodes_States: %s, %d\n", "", outarr->cDims);
}
else {
printf( "JNI GetNodes_States Access Error:%X\n", hresult);
outobj = NULL;
}
delete[] indexArray;
1) Can I pass or cast the SAFEARRAY (or VARIANT) directly to a jobject without iterating through the nodes in C++?
Absolutely not, I'm afraid. You're going to walk through the array, extract all the necessary values, and convert each of them to something that Java will understand.
2) Am I using the wrong parameters to get the SAFEARRAY data? Does the size of the access pointer (var) need to be allocated beforehand?
The most suspicious argument is indexArray, which you're setting to 0 for each dimension. However, if the array was created by Visual Basic it is quite possible that it is a 1-based array instead of a 0-based array, which would make an index of 0 illegal.
This is why your element-extraction code needs to pay attention to the results of SafeArrayGetLBound and SafeArrayGetUBound.

Copying a value in an array to a variable

I have an array of type int. It has one place and I want to copy that to a variable of type int.
Ex:
Random randomNum = new Random();
int myNumber = randomNum.Next(1,1000);
int[] puterNumber = new int[1];
puterNumber[ 1 ] = myNumber;
Later in my code..
int myPuterNumber;
myPuterNumber = puterNumber[ 1 ];
I get a message on mouseover saying 'Cannot resolve symbol 'puterNumber' '
Am I missing a step for copying the value of the array to an int variable?
This looks like a scope error, probably due to object structure if you are using Java. Honestly more code would help make it clear where exactly the problem was originating from.