Import char** z biblioteki dll

0

Witam,

Biblioteka DLL eksportuje taką oto funkcję:

extern "C"   void  __import __stdcall ReadValue(char *name, char **res, int *success);

Przykład użycia w C++ (działa ok):

 int n;
 char *res = new char[256];
 ReadValue("1",&res,&n);
 printf("Odczytano: %s\n\n",res);
.....

Próbuję teraz zaimportować ta funkcje w C#. Udalo mi sie zrobic tak, zeby kompilowalo sie bez bledu, ale wyswietla mi krzaki zamiast wyniku.

Robie tak:

[DllImport("shmem.dll")]
unsafe static extern void ReadValue(char[] name, char **res, int *success);

I użycie tego:

unsafe
            {
                int success;
                char* res;

                string s = "1";
                char[] name = s.ToCharArray();

                ReadValue(name, &res, &success);

                MessageBox.Show(Convert.ToString(success));
                MessageBox.Show(new string(res));

            }

Okazuje się, że wartosc zmiennej success jest taka, jak powinna być. Jednak wyswietlenie zmiennej res to zawsze śmieci (drugi message box).
Co jest tutaj nie tak?

[edit]
Po zastanowieniu - powyzszy kod i w C++ wywalalby smieci. Kluczowa rzecz, to jak zaalokowac pamiec, bo w c++ mam tak:

char *res = new char[256];

Ale jak zaalokowac pamiec w bloku unsafe w C#? Jednym slowem, jaki bedzie odpowiednik tej instrukcji w C#?

Chyba ze w ogole nie tak wszystko robie, wiec prosze o wyjasnienie - w C# mam male doswiadczenie.

0

A po co korzystać ze wskaźników?

Import funkcji będzie taki:

extern static void ReadValue(string name, out string res, out bool success);

A korzystanie po prostu takie:

string res;
bool success;
ReadValue("1", out res, out success);
0

Dziala.... a mozesz troche wiecej napsiac, dlaczego ma byc wlasnie tak? Wiem ze mozliwe jest przejscie char->string, widze ze wskaznik to out?

No i teoretycznie przez unsafe tez powinno chyba sie jakos dac?

0

out to referencja, nie wskaźnik jako taki. Coś bardziej w stylu C++ owego & niż pochodzącego z C *. A char ** to wskaźnik na wskaźnik tablicy char, czyli w sumie referencja na stringa jeżeli dobrze rozumiem.

0

Ok, ja jednak i tak chcialbym wiedziec jak zrobic to na wskaznikach. Bo wydaje mi sie, ze w ogolnym przypadku bez unsafe sie nie obejdzie, zwlaszcza w przypadku jeszcze bardziej pietrowych wskaznikow albo struktur. No chyba ze da sie jeszcze inaczej.

Jak mozna by bylo poprawic moj pierwszy kod? Pytam teraz w celach czysto edukacyjnych, bo ta wiedza predzej czy pozniej mi sie przyda... Bo widze, ze pomimo istnienia unsafe tak zupelnie swobodnie na wskaznikach operowac nie mozna. Dlaczego w poprzednim przypadku dostawalem tylko smieci?

0
othello napisał(a)

Ok, ja jednak i tak chcialbym wiedziec jak zrobic to na wskaznikach. Bo wydaje mi sie, ze w ogolnym przypadku bez unsafe sie nie obejdzie, zwlaszcza w przypadku jeszcze bardziej pietrowych wskaznikow albo struktur. No chyba ze da sie jeszcze inaczej.

Jak mozna by bylo poprawic moj pierwszy kod? Pytam teraz w celach czysto edukacyjnych, bo ta wiedza predzej czy pozniej mi sie przyda... Bo widze, ze pomimo istnienia unsafe tak zupelnie swobodnie na wskaznikach operowac nie mozna. Dlaczego w poprzednim przypadku dostawalem tylko smieci?

MessageBox.Show(new string(res));

na

MessageBox.Show(Marshal.PtrToStringAnsi(new IntPtr(res)));

Wydaje mi się, że to przez to, że konstruktor System.String próbuje narzucić UTF-8 z char*.
Żeby działało poprzednim kodem, funkcja w C++ musiałaby dawać np. wchar_t**

0

Rev.pl: punkt dla ciebie, miales racje. Dobrze dziala cos takiego:

[DllImport("shmem.dll")]
        unsafe static extern void ReadValue(char* name, char** res, int* success);

unsafe
            {
                int success;
                char* res;

                ReadValue((char*)Marshal.StringToCoTaskMemAnsi("1"), &res, &success);

                if(success==1)
                   MessageBox.Show(Marshal.PtrToStringAnsi(new IntPtr(res)));
                else
                   MessageBox.Show("Wartość nie istnieje", "Błąd");          
            }

Jedna kilka rzeczy jest flustrujacych:

  • czy deklaracja char* musi byc tak karkołomna? char* name = "1"; daje błąd, bo nie mozna przeprowadzic konwersji ze string na char*
  • dlaczego if(success) to błąd (nie mozna przeprowadzic konwersji z int na bool) a z kolei if(success==1) to juz dobrze (wtedy juz mozna podac int do if-a) :|
0
othello napisał(a)
  • czy deklaracja char* musi byc tak karkołomna? char* name = "1"; daje błąd, bo nie mozna przeprowadzic konwersji ze string na char*
    Tak. C# odchodzi od wskaźników. Wystarczy posługiwać się wyłącznie wygodną klasą string. Do bardziej zaawansowanej edycji łańcuchów są klasy z przestrzeni System.Text takie jak StringBuilder czy TextWriter o parserach nie wspominając.
othello napisał(a)
  • dlaczego if(success) to błąd (nie mozna przeprowadzic konwersji z int na bool) a z kolei if(success==1) to juz dobrze (wtedy juz mozna podac int do if-a) :|
    </quote>W C# nie ma automatycznej konwersji na bool. if przyjmuje bool, operator == zwraca bool więc drugi zapis jest ok.
0

Tak. C# odchodzi od wskaźników.

Tak, zdaję sobie z tego sprawę. W zasadzie bardzo rzadko używając C# wskaźniki w ogole są do czegokolwiek potrzebne.

Jednak jeżeli importuję coś z dll napisanego w C, to jest właśnie jedna z sytuacji kiedy wskaźniki mogą się przydać.

I jeszcze jedno głupie pytanie - czy da się importować klasę z natywnej biblioteki dll? (gdyby na przykład eksportowana funkcja zwracała std::string). Wydaje mi się że raczej nie, ale może jednak?

0
othello napisał(a)

Tak. C# odchodzi od wskaźników.
I jeszcze jedno głupie pytanie - czy da się importować klasę z natywnej biblioteki dll? (gdyby na przykład eksportowana funkcja zwracała std::string). Wydaje mi się że raczej nie, ale może jednak?
Nie można. Zdaje się że można co najwyżej napisać wrapper w C++/CLI który natywną klasę z dll połączy .net'owym interfejsem z programem w C#. Również w drugą stronę taki trick jest możliwy. Całe to zamieszanie wynika ze zmienionej logiki zarządzania pamięcią a dokładniej w c++ programista sam zarządza pamięcią, w C# jest garbage collector i programista nie przejmuje się zbytnio pamięcią. Dlatego potrzebny jest wrapper, który to połączy np. niezarządzane char* zamieni na zarządzane string.

Wrappery do funkcji bibliotecznych są generowane automatycznie lub są już gdzieś zapisane, dlatego dla MessageBox'a wystarczy napisać taki nagłówek jak Rev.pl pokazał na początku wątku.

1 użytkowników online, w tym zalogowanych: 0, gości: 1