Използване на DLLImport с char** и float**

Опитвам се да използвам DLL файл в C# програма, използвайки DLLImport. Имам следната функция в DLL:

int method(char* method, char** params, int n_params, float** res_arr, int* n_res);

Извикването на функция трябва да бъде нещо подобно:

method   = "method1"
char**   = {"param1=1", "param2=2"}
n_params = 2
res_arr  = the DLL function allocates an array and points this to it
n_res    = the DLL function sets to the number of results

Има отделна функция за освобождаване на float**.
Текущият ми код в C# е следният:

private static extern int method(string method, ref IntPtr params, Int32 n_params, ref IntPtr res_arr, IntPtr n_res);

Аз съм нов в C# (и моите познания по C са малко недостатъчни) и мога за живота си да не разбера как да извикам тази функция (изграждам клавиатурата си от два дни). Може ли някой да ми даде пример как трябва да стане това и как да извикам функцията?

Основният ми проблем е какво да правя с char** и float**, не знам дали това са правилните типове указатели в декларацията и не знам как трябва да създам и изпратя своя char** на функцията.

Струва си да се отбележи, че може да не променя нищо в DLL файла.

РЕДАКТИРАНЕ
Това е описанието на функцията, която освобождава резултатния масив:

free_results(float* res_arr)

EDIT2 Вече мога да извикам метода и да си върна стойностите, сега проблемът ми е, че изглежда имам проблем с достъпа до плаващите стойности. Както беше предложено, използвам Marshal.Copy() така:

[DllImport("libs\\myDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int method(string method, string[] params, Int32 n_params, out IntPtr res_arr, ref int n_res);

IntPtr res_arr = IntPtr.Zero;  
int n_res = 0;
string[] s = new string[] { "param1" };  

method("analyze", s, s.Length, out res_arr, ref n_res);  

float[] f_res = new float[n_res];
Marshal.Copy(res_arr, f_res, 0, n_res);

Проблемът е, че изглежда, че получавам само ненужни стойности във вектора с поплавък. Например в един случай трябва да получа 100.0, но получавам 15.0 или 3840.0, което ми казва, че или използвам показалеца погрешно при копиране, или има нещо друго подозрително. Кодът в DLL работи както трябва, тъй като има друга програма, написана на C, която получава правилните стойности. Имам чувството, че правя плувка на показалеца, а не това, към което сочи.


person patrick.elmquist    schedule 14.10.2013    source източник
comment
Не можете да активирате тази функция, аргументът res_arr е вашият враг. DLL разпределя масива, не можете да го освободите отново. Необходима е C++/CLI обвивка и изходният код за DLL, необходими, за да можете да изградите отново DLL и да се уверите, че използва точно същата CRT версия като вашата C++/CLI обвивка. След като имате изходния код, можете всъщност да поправите тази функция, така че да използва буфер, който подавате, вместо да разпределя такъв.   -  person Hans Passant    schedule 14.10.2013
comment
@HansPassant защо не? Изглежда, че има отделна функция за освобождаване на масива.   -  person Anton Tykhyy    schedule 14.10.2013
comment
Наистина има функция free_results(), която да извикате, за да освободите паметта.   -  person patrick.elmquist    schedule 14.10.2013
comment
Включете подписа за това как да освободите масива, тъй като това ще е необходимо и за прилагане на пълното решение.   -  person Scott Chamberlain    schedule 14.10.2013
comment
Тогава ти си напред. Използвайте string[] за параметри и изход IntPtr за res_arr. Marshal.Copy() за копиране на неуправлявания масив в float[]. Предайте този IntPtr на free_results().   -  person Hans Passant    schedule 14.10.2013
comment
Ако искате да сте много изискани, накарайте res_arr да върне персонализиран SafeHandle клас, който извиква free_results(), когато обектът е изхвърлен/финализиран.   -  person Scott Chamberlain    schedule 14.10.2013
comment
@HansPassant Това донякъде проработи, сега мога да извикам метода, но имам проблеми с използването на масива с резултати. Актуализирах описанието на проблема.   -  person patrick.elmquist    schedule 15.10.2013
comment
Доста неясно как са свързани n_res и n_results, не може да работи както е публикувано. Винаги публикувайте истински код, никога не го въвеждайте отново.   -  person Hans Passant    schedule 15.10.2013
comment
Те са едно и също нещо, коригирано сега.   -  person patrick.elmquist    schedule 15.10.2013
comment
Вашият код е наред. Напишете тривиален DLL, за да докажете, че взаимодействието е правилно.   -  person David Heffernan    schedule 15.10.2013
comment
Има много възможни ненужни стойности за стойности с плаваща запетая. Получаването на хубави кръгли числа като 15 и 3840 не означава боклук.   -  person Hans Passant    schedule 15.10.2013
comment
Частта, която е боклук, е фактът, че те са различни всеки път, когато стартирам програмата и знам със сигурност, че трябва да са 100.0 всеки път. Трябва да има само един елемент в res_arr (което n_res също показва, че има), но частта от стойностите е различна всеки път, сякаш копирам стойността на указателя, а не това, към което указателят сочи.   -  person patrick.elmquist    schedule 16.10.2013
comment
Единствената част от този проблем, който е боклук, съм аз... Има друга функция в DLL, която трябва да бъде извикана преди method(...), която бях пропуснал. С добавянето на това обаждане всичко работи като чар. Благодаря много за помощта!   -  person patrick.elmquist    schedule 16.10.2013


Отговори (1)


Това е кодът, който реши първоначалния ми проблем (както беше предложено от Hans Passant):

[DllImport("libs\\myDll.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int method(string method, string[] params, Int32 n_params, out IntPtr res_arr, ref int n_res);

IntPtr res_arr = IntPtr.Zero;  
int n_res = 0;
string[] s = new string[] { "param1" };  

method("analyze", s, s.Length, out res_arr, ref n_res);  

float[] f_res = new float[n_res];
Marshal.Copy(res_arr, f_res, 0, n_res);

Вторият ми проблем, при който float масивът даде ненужни стойности, беше резултат от това, че бях пълен задник. Има друга функция в DLL, която трябва да бъде извикана преди да се използва method(...), за да има стойности за обработка. След добавянето на това обаждане всичко работи перфектно.

person patrick.elmquist    schedule 16.10.2013