С# IteropServices с С++ dll - работает в Excel?

У меня есть dll C++, которую я пытаюсь заставить работать в своем проекте C# (я ничего не знаю о C++/C, но имею исходный код и могу открыть и собрать его в VS2013)

DLL поставляется с рабочими примерами в Excel VBA, но я не могу заставить ее работать на C #.

Ошибка, с которой я постоянно сталкиваюсь, заключается в том, что...

Дополнительная информация: вызов функции PInvoke 'RMTest!RMTest.PInvokeTest::encode_eib_d' привел к разбалансировке стека. Вероятно, это связано с тем, что управляемая подпись PInvoke не соответствует неуправляемой.

Мой тестовый код С# выглядит следующим образом...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;

namespace RMTest
{
    class Program
    {
        static void Main(string[] args)
        {
            string s1 = "2";
            string s2 = "1";
            string s3 = "1";
            string s4 = "1";
            string s5 = "TESTTE";

            var res = PInvokeTest.encode_eib_d(s1, s2, s3, s4, s5);
        }
    }

    class PInvokeTest
    {
        [DllImport("UKRM_EncodeEIB_D.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
        public static extern string encode_eib_d(string infoID, string versionID, string format, string sClass, string licNum);
    }
}

Для справки, рабочий пример в Excel, VBA выглядит следующим образом...

Public Declare _
Function encode_eib_d Lib "UKRM_EncodeEIB_D.dll" (ByVal inInfoID As String, ByVal inVersionID As String, ByVal inClass As String, _
    ByVal inFormat As String, ByVal inLicenseNum As String) As Variant

Function ENCODE_D(inInfoID As String, inVersionID As String, inFormat As String, inClass As String, inLicenseNum As String) As Variant
    Dim error As Integer

    ChDrive (ActivePath())
    ChDir (ActivePath())

    On Error GoTo ErrHandler:
    ENCODE_D = encode_eib_d(inInfoID, inVersionID, inFormat, inClass, inLicenseNum)

ErrHandler:
    If Err.Number <> 0 Then
        ENCODE_D = Err.Description
    End If


End Function

Соответствующий фрагмент кода в dll С++ (насколько я могу судить) выглядит следующим образом...

Заголовок (.h):

#include "UKRM_EncodeEIB_D.h"
#include <WTypes.h>
#include <tchar.h>
#include <comutil.h>
#include <malloc.h>
#include <atlconv.h>

extern "C" {
 VARIANT __stdcall CallEncode(LPCSTR sInfoID, LPCSTR sVersionId, LPCSTR sFormat, LPCSTR sClass, LPCSTR sLicenseNum);
}

.cpp

#include "UKRM_EncodeEIB_D_DLL.h"


// ******************************************************************************
// * CallEncode
// ******************************************************************************
///
/// @brief Use of UKRM_Encode_EIB_D to provide DLL interface
///
/// @param [in]       sInfoID        barcode info ID
/// @param [in]       sVersionId     barcode version id
/// @param [in]       sFormat        barcode format
/// @param [in]       sClass         barcode class ID
/// @param [in]       sLicenseNum    barcode license number
///
/// @return @arg returns barcode if successful, otherwise returns error
///
/// @LM_Detailed_Description
/// Creates UKRM_Encode_EIB_D object, supplies input and returns result
//
// ******************************************************************************

VARIANT __stdcall CallEncode(LPCSTR sInfoID, LPCSTR sVersionId, LPCSTR sFormat, LPCSTR sClass, LPCSTR sLicenseNum)
{
  bool bRC;
  class UKRM_Encode_EIB_D stEncodeObject;
  char *srtnstring = NULL;

  bRC = stEncodeObject.encode((char *) sInfoID, (char *) sVersionId, (char *) sFormat, (char *) sClass, (char *) sLicenseNum);

  if(bRC == true)
  {
    stEncodeObject.get_BarcodeString(&srtnstring);
  }
  else
  {
    stEncodeObject.get_ErrorString(&srtnstring);
  }

  _bstr_t bstrt(srtnstring);

  return _variant_t(bstrt).Detach();
}

.деф:

LIBRARY "UKRM_EncodeEIB_D"
EXPORTS
encode_eib_d = CallEncode
VERSION 1.0

Я пробовал различные комбинации MarshalAs и т. д. для входных параметров, а также пробовал StringBuilders вместо строк, но звучит так, как будто он не доволен сигнатурой метода от одного к другому.

Настолько расстраивает, что это работает в VBA.

Если вам нужна дополнительная информация, пожалуйста, спросите.

Спасибо.


person Dave S    schedule 20.01.2015    source источник
comment
Вы имели в виду переключить имена параметра 3/4 вызовов PInvoke? Также вызов VB возвращает вариант, но при вызове C# вы возвращаете строку. Ближайшим соответствием в C# является object.   -  person Robert Snyder    schedule 20.01.2015
comment
Это из-за возвращаемого типа. Variant — это структура, которую вы по какой-то причине произвольно превратили в строку. Вам нужно изменить его на object и применить атрибут [return:MarshalAs(UnmanagedType.Struct)].   -  person Hans Passant    schedule 20.01.2015
comment
@Hans - я использовал приведенный ниже код, опубликованный Jcl, но изменил тип возвращаемого значения на указанный выше объект и возвращаемый атрибут... Теперь я получаю сообщение об ошибке Дополнительная информация: ограничение PInvoke: не может возвращать варианты.   -  person Dave S    schedule 20.01.2015


Ответы (1)


В вашем случае вам понадобится подпись pinvoke, например:

[DllImport("UKRM_EncodeEIB_D.dll", CallingConvention = CallingConvention.StdCall)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string encode_eib_d(
          [MarshalAs(UnmanagedType.LPStr)]string infoID, 
          [MarshalAs(UnmanagedType.LPStr)]string versionID, 
          [MarshalAs(UnmanagedType.LPStr)]string format, 
          [MarshalAs(UnmanagedType.LPStr)]string sClass, 
          [MarshalAs(UnmanagedType.LPStr)]string licNum);
person Jcl    schedule 20.01.2015
comment
Упс, верно, я думал LPCTSTR. Будет редактировать - person Jcl; 20.01.2015