Можно ли ссылаться на проект С++ из проекта С#?

Я пытаюсь вызвать некоторый С++, который я написал с некоторого С# (также мой код), и у меня возникают проблемы с Interop/PInvoke, возвращающим заполненный int[] обратно. Есть ли способ просто добавить ссылку времени компиляции на проект С++ из проекта С# и вызвать функции внутри него, как если бы это была просто еще одна библиотека классов С#?

У меня есть базовый пример, иллюстрирующий проблему с массивом. Это подпись C# для функции C++, которую я вызываю.

    [DllImport("CPlusPlusTargetLibrary.dll", CallingConvention = CallingConvention.StdCall)]
    private static extern void Accumulate
    (
        int input,
        int accumulationFactor,
        int numberOfAccumulations,
        [In, Out, MarshalAs(UnmanagedType.LPArray)]
        int[] accumulationsPointer
    );

Вот как это вызывается из C#:

        var numberOfAccumulations = 3;
        var accumulations = new int[numberOfAccumulations];

        Accumulate(-8, 1, numberOfAccumulations, accumulations);

Это его объявление в заголовочном файле C++:

__declspec(dllexport) const void __stdcall Accumulate
(
    const signed long input,
    const signed long accumulationFactor,
    const signed long numberOfAccumulations,
    signed long* accumulationsPointer
);

А это его реализация на C++:

__declspec(dllexport) const void __stdcall Accumulate
(
    const signed long input,
    const signed long accumulationFactor,
    const signed long numberOfAccumulations,
    signed long* accumulationsPointer
)
{
    for (auto index = 0; index < numberOfAccumulations; index++, accumulationsPointer++)
    {
        auto accumulation = input * ((index + 1) * accumulationFactor);

        accumulationsPointer = &accumulation;            
    }
}

Массив accumulations просто возвращается как трехэлементный массив всех 0 - точно так же, как он был передан. Вместо этого он должен был вернуться, содержащий -8, -16 и -24.

Я следовал документации в MSDN для маршалинга int[], и, согласно ей, мне не нужно было маршалировать вручную (но даже удаление атрибута MarshalAs не решило проблему): https://docs.microsoft.com/en-us/dotnet/framework/interop/marshaling-other-types-of-arrays

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


person Matt Arnold    schedule 12.01.2021    source источник
comment
Только если это управляемый С++, а я предполагаю, что это не так.   -  person Matthew Watson    schedule 12.01.2021
comment
Я готов сделать его управляемым C++.   -  person Matt Arnold    schedule 12.01.2021
comment
Превращение C++ в управляемый C++ требует примерно тех же усилий, что и преобразование C++ в C#.   -  person Eljay    schedule 12.01.2021
comment
Вы все равно столкнетесь с теми же проблемами, потому что это все еще проект C++, а сигнатуры методов не изменились — у вас все еще будут проблемы с Interop/PInvoke. Вам нужно будет либо правильно вызвать метод, либо преобразовать код C++ в C#.   -  person Russ    schedule 12.01.2021
comment
К сожалению, мой код C++ вызывает код C++ для драйвера, поэтому я не могу избежать проблем взаимодействия, используя весь C#.   -  person Matt Arnold    schedule 12.01.2021
comment
Каково объявление метода С++, который вы пытаетесь вызвать?   -  person Matthew Watson    schedule 12.01.2021
comment
Опубликуйте соответствующие фрагменты кода, как вызываемую экспортированную функцию C++, так и вызывающую программу C#.   -  person Alejandro    schedule 12.01.2021
comment
Ваша подпись C# не соответствует подписи C++. long не является int. В C# вам нужно использовать long.   -  person Russ    schedule 12.01.2021
comment
@Russ long в C++ равен int в C#: displayfusion.com/Discussions/View/converting-c-data-types-to-c/ long в C# на самом деле long long в C++!   -  person Matt Arnold    schedule 12.01.2021
comment
@Russ Я все равно пытался изменить все свои long на int из любопытства (я не знал, что int также был 32-битным int в C ++), но результат все равно был прежним.   -  person Matt Arnold    schedule 12.01.2021
comment
Ну, это была мысль. Я не работаю с C++, поэтому меня удивило, что long в C++ были 32-битными.   -  person Russ    schedule 12.01.2021
comment
разве код С++ accumulationsPointer = &accumulation; не должен быть *accumulationsPointer = accumulation;?   -  person apple apple    schedule 12.01.2021
comment
ну, не совсем уверен, что должен делать код, но, конечно, он никогда не записывает содержимое accumulationsPointer   -  person apple apple    schedule 12.01.2021
comment
(или accumulationsPointer[index] = accumulation, без accumulationsPointer++ )   -  person apple apple    schedule 12.01.2021
comment
@appleapple Ты был прав! Это связано с тем, что я обычно не пишу код на C++ - ранее я пробовал accumulationsPointer* = accumulation и получал ошибку компилятора expected an expression, поэтому я предполагал, что значения указателя доступны только для чтения в C++. Теперь я возвращаю ожидаемые значения с вашим предложением. Я с радостью отмечу это как ответ на мою проблему, хотя я не знаю, должен ли я теперь изменить название вопроса, чтобы помочь другим в будущем прийти к этому.   -  person Matt Arnold    schedule 12.01.2021
comment
Не могли бы вы сопоставить указатель функции с делегатом в C#? видеть; stackoverflow.com/questions/39790977/   -  person Oliver Ciappara    schedule 12.01.2021
comment
Я не уверен, что следую - какая польза от этого?   -  person Matt Arnold    schedule 12.01.2021


Ответы (1)


ваш код не записывает содержимое accumulationsPointer, а перезаписывает сам указатель адресом accumulation


это должно быть что-то вроде этого

for (auto index = 0; index < numberOfAccumulations; index++, accumulationsPointer++)
{
   auto accumulation = input * ((index + 1) * accumulationFactor);
   *accumulationsPointer = accumulation;
}

или вот так, как в c#

for (auto index = 0; index < numberOfAccumulations; ++index)
{
   auto accumulation = input * ((index + 1) * accumulationFactor);   
   accumulationsPointer[index] = accumulation;            
}

Кстати, signed long* accumulationsPointer также может быть записано как signed long accumulationsPointer[], например.

void Accumulate
(
   const signed long input,
   const signed long accumulationFactor,
   const signed long numberOfAccumulations,
   signed long accumulations[]
)
{
   for (auto index = 0; index < numberOfAccumulations; ++index)
   {
      auto accumulation = input * ((index + 1) * accumulationFactor);
      accumulations[index] = accumulation;            
   }
}
person apple apple    schedule 12.01.2021