.Net Compact Framework - вызов объекта ActiveX, использующего [out] SAFEARRAY (float) * - PullRequest
1 голос
/ 29 октября 2010

В Compact Framework 3.5 я пытаюсь вызвать объект ActiveX, имеющий сигнатуру функции IDL:

HRESULT MyFunc([out] SAFEARRAY(float) *var)

Генерация Interop создает msil

[out] class [mscorlib]System.Array&  marshal( safearray float32)

, который кажетсядостаточно разумно, но я продолжаю получать "NotSupportedException".Согласно статье под названием «Взаимодействие: общие проблемы и методы отладки» (я не могу опубликовать более одной гиперссылки, это первый результат Google для этой фразы), в первом пункте под заголовком «Marshaling» - компактная структуранеправильно назначает SAFEARRAYs.

Я пытался обойти эту проблему, манипулируя ответом, описанным в этом сообщении на форуме MSDN (последняя запись описывает его метод): http://social.msdn.microsoft.com/forums/en-US/clr/thread/6641abfc-3a9c-4976-a523-43890b2b79a2/

ИтакЯ создал следующее определение:

[StructLayout(LayoutKind.Sequential)]
struct SafeArray
{
    public ushort dimensions;     // Count of dimensions in the SAFEARRAY
    public ushort features;       // Flags to describe SAFEARRAY usage
    public uint elementSize;    // Size of an array element
    public uint locks;          // Number of times locked without unlocking
    public IntPtr dataPtr;        // Pointer to the array data
    public uint elementCount;   // Element count for first (only) dimension
    public int lowerBound;     // Lower bound for first (only) dimension
}

И переопределил IDL для сигнатуры функции:

HRESULT MyFunc([out] long *var)

И затем выдал следующий код:

IntPtr safeArrayPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(SafeArray)));
SafeArray safeArray;
safeArray.dimensions = 1;
safeArray.features = 0;
safeArray.elementSize = (uint)(Marshal.SizeOf(typeof(float)));
safeArray.locks = 0;
safeArray.elementCount = 6;
safeArray.lowerBound = 0;
safeArray.dataPtr = Marshal.AllocCoTaskMem((int)(safeArray.elementCount * safeArray.elementSize));

Marshal.StructureToPtr(safeArray, safeArrayPtr, false);
int iTmp = safeArrayPtr.ToInt32();
MyFunc(out iTmp)

В то время как код кажется успешным, когда я пытаюсь прочитать значения данных, используя функцию Marshal.Copy (dataPtr, myFloatArr, false), я получаю все 0 для данных, что говорит мне, что указательActiveX DLL, вероятно, является полностью поддельным, и он забывает писать.

Любые предложения относительно того, что у меня может бытьв этих определениях или предложениях по другим способам решения этой проблемы?

Заранее спасибо ...

1 Ответ

1 голос
/ 10 ноября 2010

Ну, я решил это.
Надеюсь, мой ответ поможет другим, кто столкнется с той же проблемой. Проблема, с которой я столкнулся, заключалась в том, что тег [out] в объявлении COM tlb означает, что все, что я передаю, будет перезаписано объектом, созданным внутри библиотеки COM. Довольно сложная версия классической (и очень элементарной задачи) «Передача по ссылке против Передачи по значению»

Итак, правильный маршалинг - использовать определение SafeArray, которое я разместил выше.

Не трогайте саму подпись IDL - это не очень чистый способ сделать это. Вместо этого используйте ildasm в сгенерированной библиотеке Interop, чтобы изменить il из:

[out] class [mscorlib]System.Array&  marshal( safearray float32)

до

[out] native int&

, а затем снова собрать с помощью ilasm, который выдаст сигнатуру функции C #

void MyFunc(out IntPtr var)

Код вызова становится:

IntPtr ip;
SafeArray safeArray;
float []arrFloats;
MyFunc(out ip);
//Marshal the structure itself
Marshal.StructureToPtr(safeArray, ip, false);

//Marshal the data over to .NET
float []arrFloats = new float[safeArray.elementCount];
Marshal.Copy(safeArray.dataPtr, arrFloats, 0, (int)safeArray.elementCount);

Наконец, нам нужно освободить память (помните, мы изменили сигнатуру функции, поэтому мы не предоставляем .NET достаточно информации, чтобы фактически освободить память самостоятельно.

//Don't forget to free both the structure and the object
Marshal.FreeCoTaskMem(safeArray.dataPtr);
Marshal.FreeCoTaskMem(ip);
...