Member 13348717 Ответов: 1

Передача сложной структуры C++ в C#


Я потратил много часов, проверяя другие статьи и экспериментируя с различными методами, пытаясь решить эту. Я пытаюсь перенести устаревший код с C++ на C#, и часть этого переноса включает в себя преобразование существующих файлов данных в новые форматы.

Из многих прочитанных мною эта статья представляется наиболее актуальной
c# - импорт функции из c++, возвращающей structure-Stack Overflow[^]
однако структуры, которые я портирую, не так просты и включают массивы.

Я также ссылался на эту статью
Разоблачение native для managed-C++ / CLI против P / Invoke[^]

Я сделал комментарии в приведенном ниже коде относительно областей, которые я в настоящее время получаю с несколькими попытками.

Главная проблема заключается в том, что основная структура также включает в себя массивы других структур, которые я с большим трудом передаю в свой код C#.

Любая помощь или указания на этот счет будут оценены по достоинству. Я постарался сделать код как можно более кратким, а также показать, в чем заключаются проблемы, и быть описательным в коде.

Единственная проблема (я думаю) - это вложенные массивы структур. C# ограничивает это объявление в структуре, поэтому мне пришлось довольствоваться указателями

Что я уже пробовал:

Это то, что я имею в настоящее время после слишком многих попыток

Код C++

namespace CPlusPlusDLL
{
#define MAX_DESCRIPTION_LENGTH 255
#define MAX_INSTALLATIONS 46
#define MAX_CONFIGURATIONS 14

#pragma pack(1)
	typedef struct
	{
		short row;
		unsigned char	column;
		short table;
	} TABLE_TYPE;

	typedef struct
	{
		struct
		{
			short version;
			char description[MAX_DESCRIPTION_LENGTH];
			char something;
		} INSULATION_HEADER;
		struct
		{
			char code[MAX_DESCRIPTION_LENGTH];
			TABLE_TYPE tableDetails[MAX_INSTALLATIONS];
		} val_struc[MAX_CONFIGURATIONS];
	} DEFINITION;

	public ref class ManagedWrapper
	{
	public:
		static DEFINITION* ReturnStructObjectFunc(int defIndex)
		{
			DEFINITION* def = new DEFINITION();

			(*def).INSULATION_HEADER.version = 1;
			strcpy_s((*def).INSULATION_HEADER.description, "description");
			(*def).INSULATION_HEADER.something = 'C';
			// ... Populate the rest of the structure here and all the arrays.		

			return def;
		};
	};

	extern "C" __declspec(dllexport) DEFINITION* ReturnStructObjectDLLImport(int defIndex)
	{
		DEFINITION* def = new DEFINITION();

		(*def).INSULATION_HEADER.version = 1;
		strcpy_s((*def).INSULATION_HEADER.description, "description");
		(*def).INSULATION_HEADER.something = 'C';
		// ... Populate the rest of the structure here and all the arrays.		

		return def;
	};
}


Код C#
namespace CSharpDLL
{
  public class Wrapper
  {
    const int maxDescLength = 255;
    const int maxInstalls = 46;
    const int maxConfigs = 14;

    [StructLayout( LayoutKind.Sequential )]
    unsafe public struct LegacyInsulationHeader
    {
      short version;
      // Can use a fixed array here as this is a primitive type.
      fixed char description[maxDescLength];     
      char something;
    }

    [StructLayout( LayoutKind.Sequential )]
    unsafe public struct LegacyTableType
    {
      short row;
      byte column;
      short table;
    };

    [StructLayout( LayoutKind.Sequential )]
    unsafe public struct LegacyConfiguration
    {
      fixed char code[maxDescLength];
      // Cannot do this because it is a struct and not a primitive type.
      fixed LegacyTableType tableDetails[maxInstalls];
      // which leaves me having to use a pointer here.
      LegacyTableType* tableDetails;                       
    }

    [StructLayout( LayoutKind.Sequential )]
    unsafe public struct LegacyDefinition
    {
      LegacyInsulationHeader INSULATION_HEADER;
      // Cannot do this because it is a struct and not a primitive type.
      fixed LegacyConfiguration val_struc[maxConfigs];  
      // which leaves me having to use a pointer here.
      LegacyConfiguration* val_struc;                      
    };

    [DllImport( "CPlusPlusDLL.dll", SetLastError = true )]
    internal static extern IntPtr ReturnStructObjectDLLImport( int insulationType );

    public static void ConvertStruct(int index)
    {
      unsafe
      {
        LegacyDefinition *definition = null;

        // This call fails to build as it is returning a struct * and I get the error
        // CPlusPlusDLL.ManagedWrapper.ReturnStructObjectFunc( 0 ) is inaccessible
        // due to its protection level
        // If I change the return type to int or void however the protection level
        // is not an issue
        definition = CPlusPlusDLL.ManagedWrapper.ReturnStructObject( 0 );

        // The following code gives me this error at run time
        // Additional information: A call to PInvoke function
        // 'InteropTesting!CSharpDLL.Wrapper::ReturnStructObjectDLLImport' has unbalanced the stack.
        // This is likely because the managed PInvoke signature does not match the unmanaged target
        // signature. Check that the calling convention and parameters of the PInvoke signature
        // match the target unmanaged signature.
        ReturnStructObjectDLLImport(index);
      }
    }
  }
}


Консольное Тестовое Приложение C#
namespace WinConsoleTestApp
{
  class Program
  {
    static void Main( string[ ] args )
    {
      int a = 0;
      CSharpDLL.Wrapper.ConvertStruct( 0 );
      a++;
    }
  }
}

1 Ответов

Рейтинг:
0

KarstenK

Одна из ваших проблем заключается в том, что вы возвращаете память (или структуры/объекты), выделенную во время выполнения C++, в C#. Это приводит к утечкам или нарушению доступа при его освобождении.

Другой - это вложенность данных, которая показывает, что ваша модель данных не очень хорошо разработана. Я бы переупорядочил дизайн, сначала извлекая количество наборов данных, а затем извлекая каждый набор данных, выделяя структуру в C# при заполнении ее в C++. Воспользуйся класс StringBuilder класс для струн.

Здесь объясняется несколько простых, но работающих кодов статья.

Еще одна вещь: общий стиль кодирования в C++ - это:

(*def).INSULATION_HEADER.version = 1;// yours and hard to read
def->INSULATION_HEADER.version = 1;//common style: pointer access