Com interface with fixed size array - nav

I need to expose a COM interface with a method that implement one fixed size array parameter.
The array size is fixed because the consumer is based on a tecnology in which you must declare the array size.
So I could do something like this
[Guid("2AE7C342-89ED-492B-B9AA-92A778332000")]
public interface _DocSolutionsClassic
{
[DispId(1)]
void Execute( string[] InputParams);
}
and it works for me, but there is a different dimension error by the consumer.
I would like to write something lik this
[DispId(1)]
void Execute( string[50] InputParams);
or this
[DispId(1)]
string[] InputParams = new string[50];
void Execute(string[] InputParams);
But Vstudio 2010 give me an error...

Perhaps this could help:
IDL code snippet:
[id(1), helpstring("method foobar")] HRESULT foobar( BSTR * bstrs, ULONG size );
This way you can send any number of strings. The 'size' parameter is actually the number of strings that 'bstrs' points to.
Note:
Use SysAllocString()/SysFreeString() for string allocation/deallocation (or some equivalent in your environment).

Related

Passing SAFEARRAY of structs containing other SAFEARRAYs

I have some structs defined in an .ild file that are packed into SAFEARRAYand passed to a C# client...
[uuid(/*omitted*/)]
struct Port {
USHORT Num;
};
[uuid(/*omitted*/)]
struct Descriptor {
BSTR Name;
SAFEARRAY(struct Port) Ports;
};
// Inteface method
HRESULT GetDescriptors([out, retval] SAFEARRAY(struct Descriptor)* descriptor)
I can unpack and use the arrays sucessfully on the C++ side. The problem is when I try to call the method from C# using the following...
var descriptors = comObject.GetDescriptors();
I get an exception stating...
System.TypeLoadException: 'Cannot marshal field 'Ports' of type
'ComObjectLib.Descriptor': There is no marshaling
support for this type.'
Here's where it gets weird. If I add a dummy instantiation of a Descriptor and initialize it's fields anywhere in the C# code, the marshalling works as expected.
var descriptor = new Descriptor();
descriptor.Ports = new Port[0];
// Works as expected.
var descriptorsFromCom = comObject.GetDescriptors();
What gives?

ERROR: 'a pointer to member is not valid for a managed class'

I'm getting this error and I don't know how to solve it. This is my code (more details below):
CP1626.h
#pragma once
#include <Windows.h>
namespace CP1626{
...
//DLL functions
void setCallbackDataWriteFunc(PNIO_CBF_DATA_WRITE _ptrDataWriteFunc);
...
}
cp1626.cpp
#include "cp1626lib.h"
...
typedef void (* ptr_setCallbackDataWriteFunc)(PNIO_CBF_DATA_WRITE);
ptr_setCallbackDataWriteFunc setCallbackDataWriteFuncFunction;
...
void CP1626::setCallbackDataWriteFunc(PNIO_CBF_DATA_WRITE _ptrDataWriteFunc){
(setCallbackDataWriteFuncFunction)(_ptrDataWriteFunc);
}
And with PNIO_IOXS defined as
typedef PNIO_IOXS (*PNIO_CBF_DATA_WRITE) /* write data to IO stack (local ==> remote) */
(PNIO_UINT32 DevHndl, /* Handle for Multidevice */
PNIO_DEV_ADDR * pAddr, /* geographical address */
PNIO_UINT32 BufLen, /* length of the submodule input data */
PNIO_UINT8 * pBuffer, /* Ptr to data buffer to write to */
PNIO_IOXS Iocs); /* remote (io controller) consumer status */
And then, I've, coded using C++/CLI and .NET, my main user interface class, who does next:
UI_Main.h
#include "cp1626lib.h"
...
public ref class UI_Main : public System::Windows::Forms::Form{
//Local function definition, which is assigned to callback
PNIO_IOXS dataWriteFunc(PNIO_UINT32 DevHndl, PNIO_DEV_ADDR * pAddr, PNIO_UINT32 BufLen, PNIO_UINT8 * pBuffer, PNIO_IOXS Iocs);
...
void InitCP1626(){
...
CP1626::setCallbackDataWriteFunc(dataWriteFunc);
...
}
}
The error is at line CP1626::setCallbackDataWriteFunc(dataWriteFunc);, and it says that a pointer to member is not valid for a managed class. What's the correct form to do this callback assignation when I'm on a managed class? I think that I should find a way to convert delegates into function pointers, but also I'm not sure about this.
Thank you in advance.
EDIT: I've tried to change line CP1626::setCallbackDataWriteFunc(dataWriteFunc); with CP1626::setCallbackDataWriteFunc(Marshal::GetFunctionPointerForDelegate(del));, with dataWriteFunc^ del; a delegate with format delegate PNIO_IOXS dataWriteFunc(PNIO_UINT32 DevHndl, PNIO_DEV_ADDR * pAddr, PNIO_UINT32 BufLen, PNIO_UINT8 * pBuffer, PNIO_IOXS Iocs);
But it also fails. At this time, the error says 'CP1626::setCallbackDataWriteFunc : cannot convert parameter 1 from 'System::IntPtr' to 'PNIO_CBF_DATA_WRITE''
you might think about rereading part of the documentation from Microsoft. Even if it's old (like from 2006) it is not outdated.
Example. Microsoft documented the use of callbacks.
https://msdn.microsoft.com/en-us/library/367eeye0.aspx
In your case, you didn't look, what GetFunctionPointerForDelegate() is returning. It is a IntPtr. This is a structure Microsoft uses to wrap all sort of pointers into one structure for .NET. It has the method void* ToPointer()
PNIO_CBF_DATA_WRITE cb = static_cast<PNIO_CBF_DATA_WRITE>(IntPtr.ToPointer());
CP1626::setCallbackDataWriteFunc(cb);
Be aware that you should use __stdcall for the callback. It is mentioned to be needed for compatibility.
typedef PNIO_IOXS (__stdcall *PNIO_CBF_DATA_WRITE)
If you are not able to recompile the sources to use __stdcall, just reroute it over another function you have defined.
I tried to use your code within a test project but i didn't want to setup to call the callback somewhere independent.
Kind Regards
p.s. I wanted to mark your question with low quality. Because there is documentation to read/google and learn from. You should take your time and be so kind to mark some of the answers for your four questions lately as the right answer.

How to use custom type function pointer in C++

Condition
I use a framework that has an custom type as bellow:
typedef log (*CustomType) (
int timeStamp,
const char* data,
int dataSize,
void* userData,
int dataType,
int viewId
)
and MyClass init method as bellow:
MyClass_Init (void **output, CustomType video, CustomType audio, void* userData)
Question
I used init method like bellow but always receive error (error content is not displayed because i use a framework). pls point me what is missed.
CustomType videoInput;
CustomType audioInput;
void *output = malloc(sizeof(void*);
void *userData = malloc(sizeof(void*));
long result = MyClass_Init(&output, videoInput, audioInput, userData);
A number of things wrong with this code:
You can't intermix function pointers and method pointers. What it boils down to is that the this for a method has to be included in the method call signature. Since the function pointer doesn't include the this pointer (it is a function, not a method pointer), the two can not match.
Most C-based API includes some sort of reference value (most frameworks call those refCon, context or userData), so what you can do is create an adapter function that calls your method. The userData parameter in your CustomType parameter list looks like it is one of those (consult the docs to be sure).
You can probably provide a userData wherever you set MyClass_Init as your callback now. So, if that function to provide a callback to the library was called set_callback( MyCustomType callback, void* userData ), do something like
MyClass *obj = new MyClass; // Or however you create your object
set_callback( MyClassCallbackAdapterFunction, obj );
with an adapter function like:
log MyClassCallbackAdapterFunction( int timeStamp, const char* data, int dataSize, void* userData, int dataType, int viewId )
{
MyClass *myThis = (MyClass*) userData;
// Here you can now call myThis->MyClass_Init( ... ) however you want to.
}
The malloc( sizeof(void*) ) statements look like you're misunderstanding return parameters (also called "side effects" by some teachers). I don't have the docs to whatever API/library you're using, but I'm pretty certain you're supposed to not just pass in buffers the size of a pointer. Either you'd just provide a pointer on the stack in which a buffer will be returned, or you provide a whole buffer (e.g. an array) and its size, and that is where the callback will write to or so.

FlasCC: interop between Vector.<Number> and double[]

Assume that I am new to AS3 and FlasCC and try to port some legacy C++ code to Flash. What I have is some number-crunching routine that takes an array of type double (the array is of fixed size). The C++ prototype is, say:
double doNumberCrunching(double input[512]);
Now I would like to build an SWC and later call doNumberCrunching() from AS3. SWIG generates the AS3 interface as:
doNumberCrunching(var _input:*):Number;
However, I have no idea how to feed it with Vector.<Number>(512) without working on raw bytes.
My question is: how to solve it neatly? Do you know a cheap way to access double* underlying the Vector.<Number>? Or something even better?
If only your number crunching function was operating on ints! Then you would be able to use CModule.writeIntVector to write vector contents to DomainMemory and just pass over a pointer to the C++ code.
But since it's doubles, you will have to iterate through Vector and convert each element from AS3 Number to C++ double using CModule.writeDouble. Then you can manually expose your function to AS3 with an interface that accepts a DomainMemory pointer:
void doNumberCrunchingAS() __attribute__((used,
annotate("as3sig:public function doNumberCrunching(inputPtr:int):Number"),
annotate("as3package:mypackage"))
));
double doNumberCrunching( double* input )
{
// Actual implementation here
return 0;
}
void doNumberCrunchingAS()
{
// Read the AS3 argument and convert it to a convenient C++ form.
int memoryOffset = 0;
AS3_GetScalarFromVar(memoryOffset, inputPtr);
double* inputPtr = reinterpret_cast<double*>( memoryOffset );
// Call an implementation routine.
double result = doNumberCrunching( inputPtr );
// Return result to AS3 code.
AS3_Return( result );
}
AS3 code:
package
{
using mypackage.doNumberCrunching;
function testNumberCrunching():void
{
var ptr:int = CModule.malloc(blobSize);
// a loop of writeDouble should be here
doNumberCrunching(ptr);
CModule.free(ptr);
}
}

Passing an array using COM?

I am a COM object written in ATL that is used from a C++ application, and I want to pass an array of BYTEs between the two. My experience of COM/IDL so far is limited to passing simple types (BSTRs, LONGs, etc.).
Is there a relatively easy way to have the COM object pass an array to the caller? For example, I want to pass a raw image (TIFF) instead of messing with temporary files.
Try passing a safearray variant to the COM Object. Something like this to put a BYTE array inside a safearray variant....
bool ArrayToVariant(CArray<BYTE, BYTE>& array, VARIANT& vtResult)
{
SAFEARRAY FAR* psarray;
SAFEARRAYBOUND sabounds[1];
sabounds[0].lLbound=0;
sabounds[0].cElements = (ULONG)array.GetSize();
long nLbound;
psarray = SafeArrayCreate(VT_UI1, 1, sabounds);
if(psarray == NULL)
return false;
for(nLbound = 0; nLbound < (long)sabounds[0].cElements ; nLbound++){
if(FAILED(SafeArrayPutElement(psarray, &nLbound, &array[nLbound]))){
SafeArrayDestroy(psarray);
return false;
}
}
VariantFree(vtResult);
vtResult.vt = VT_ARRAY|VT_UI1;
vtResult.parray = psarray;
return true;
}
SAFEARRAYs are the way to go if you want OLE-Automation compliance, and maybe use the COM interface from other languages such as VB6. But there is an alternative in IDL, for example: -
void Fx([in] long cItems, [in, size_is(cItems)] BYTE aItems[]);
This describes a method where the marshalling code can infer the number of bytes to be transfered by inspecting the value of the first parameter.
This is fine if your clients are all written in C/C++, but i think that an interface containing this would not be automation-compliant, so not usable from VB6, and possibly the standard marshaler will not be able to do the marshaling, so you'd need to generate your own proxy/stub DLL from the IDL. Not hard to do, but a bit harder than using SAFEARRAYs.
Check out using safearrays. Here's some example code:
The safearray is returned as a pointer to a VARIANT
[id(1), helpstring("LogCache")] HRESULT LogCache([out,retval] VARIANT* logCache);
Safearrays are pretty easy to use. Here's some example code which is a cache of the latest 1000 log messages of some application:
safearray_t<bstr_t> m_logCache;
...
if (m_logCache.size() > 1000)
{
m_logCache.pop_back();
}
m_logCache.push_front(Msg.str(), 0);
variant_t LogCache()
{
if (!m_logCache.is_empty())
{
variant_t cache(m_logCache);
return cache;
}
}
Note that the syntax in your case is almost certainly going to be different since I'm using the comet COM library, but the ideas/concepts are the same.
You can use BSTR to pass an array of bytes.
BYTE array[buffer_size];
...
BSTR toBePassed = SysAllocStringByteLen((OLECHAR*)array,length);
YourCOMMethod(toBePassed);
SysFreeString(toBePassed);
In your method:
BYTE* pData = (BYTE*)bstrPassed;
DWORD dataLength = SysStringByteLen(bstrPassed);