#c# #c #arrays #dll #dllimport
#c# #c #массивы #dll #dllimport
Вопрос:
Я создаю DLL-оболочку для некоторого кода на C и столкнулся с проблемой. Мне будет легче проиллюстрировать мою проблему после предоставления некоторого примера кода.
В файле .h моего приложения C :
#pragma once
namespace Wrapper {
extern __declspec(dllexport)
void SANDBOX(int arrayLength, int* intArray);
}
В cpp-файле моего приложения C :
#include "stdafx.h"
#include "Wrapper.h"
#include <windows.h>
using namespace Wrapper;
extern "C"{
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
return TRUE;
}
__declspec(dllexport) void SANDBOX(int* arrayLength, int* intArray[])
{
if(*intArray == NULL)
{
*arrayLength = 5;
return;
}
else
{
int i = 0;
for(i = 0; i < (*arrayLength); i )
{
(*intArray)[i] = 1 i*i;
}
}
}
}
Мое приложение C # импортирует метод следующим образом:
const string dllFileLocation = "Wrapper.dll";
[DllImport(dllFileLocation, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void SANDBOX(ref int arrayLength, ref int[] intArray);
И, наконец, мое приложение C # вызывает код в форме, подобной этой:
//Initialize some things
string outString;
int numInArray = -1;
int[] iArray = null;
//Fill the numInArray integer
Wrapper_Handle.SANDBOX(ref numInArray, ref iArray);
//Lets initialize the array and print the values
iArray = new int[numInArray];
outString = "";
foreach(int i in iArray)
{
outString = i "n";
}
MessageBox.Show(outString);
//Now lets put values into the array and print the new ones out
Wrapper_Handle.SANDBOX(ref numInArray, ref iArray);
outString = "";
foreach(int i in iArray)
{
outString = i "n";
}
MessageBox.Show(outString);
Итак, чего я ожидал бы, так это того, что я создам массив null, чтобы получить количество значений для инициализации. Итак, я получаю 5, создаю новый массив и печатаю его. Он получает то, что я ожидал: окно сообщения с 0 пять раз. Однако после повторного вызова метода во втором окне сообщения отображается просто число 1, без каких-либо других значений. Может показаться, что остальные 4 значения «отрезаны» от массива, и приложение C # больше не может их видеть.
Я успешно импортировал множество других функций из C .DLL. Именно эта нотация массива вызывает проблемы. При отладке .В DLL значения правильно передаются в код C , и они присваиваются.
Does anyone know why the C# application «loses» the other 4 values? It does not matter the length of the array, only the first value is ever kept after the call to the method.
Thanks in advance. Any help is greatly appreciated.
EDIT—
The solution to my problem was provided in a comment to this question. I am posting my changes below:
C .h file:
#pragma once
namespace Wrapper {
extern __declspec(dllexport)
void SANDBOX(int arrayLength, int* intArray);
}
Файл C .cpp:
#include "stdafx.h"
#include "Wrapper.h"
#include <windows.h>
using namespace Wrapper;
extern "C"{
BOOL APIENTRY DllMain(
HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved )
{
return TRUE;
}
__declspec(dllexport) void SANDBOX(int* arrayLength, int* intArray)
{
if(intArray == NULL)
{
*arrayLength = 5;
return;
}
else
{
int i = 0;
for(i = 0; i < (*arrayLength); i )
{
(intArray)[i] = 1 i*i;
}
}
}
}
Импорт C #:
const string dllFileLocation = "Wrapper.dll";
[DllImport(dllFileLocation, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public extern static void SANDBOX(ref int arrayLength, int[] intArray);
C # вызывает:
string outString;
int numInArray = -1;
int[] iArray = null;
Wrapper_Handle.SANDBOX(ref numInArray, iArray);
iArray = new int[numInArray];
outString = "";
foreach(int i in iArray)
{
outString = i "n";
}
MessageBox.Show(outString);
Wrapper_Handle.SANDBOX(ref numInArray, iArray);
outString = "";
foreach(int i in iArray)
{
outString = i "n";
}
MessageBox.Show(outString);
Комментарии:
1. Маршаллер pinvoke не знает, насколько большим может быть массив. Ваш C-код на самом деле не создает массив (хорошо), поэтому ваши объявления неуместны. Удалите [] из объявления C. Удалите ref из объявления C #.
2. Ваши изменения создали эффект, который я искал. Я все еще пытаюсь разобраться в нюансах маршаллера PInvoke. Большое вам спасибо!
Ответ №1:
Передача массива по ссылке в P / Invoke является немного странной операцией, поскольку предположительно она позволяет вызываемой функции заменять ссылку с собственной стороны. Я не совсем уверен, почему опубликованный вами код не работает, поскольку он кажется разумным (кроме ранее упомянутой странности с ref и объявления в вашем заголовке, не соответствующего типам параметров).
Однако, я думаю, вы можете легко изменить код, чтобы устранить проблему, не передавая по ссылке, а просто взяв указатель на массив. Вы не пытаетесь переназначить саму ссылку на массив; вы просто заботитесь об изменении его содержимого.
Комментарии:
1. Я попробовал это (используя указатели), и это действительно решает мою проблему. Однако это помещает вещи в небезопасный контекст, которого я пытаюсь избежать. Тем не менее, спасибо!
2. @user2340633: Обратите внимание, что изменения, рекомендованные MikeP, были точно такими же, как прокомментировал Ханс. «не передавать по ссылке = удалить ссылку из объявления C #» «просто возьмите указатель = удалить [] из объявления C»