Eksport funkcji zwracającej string

1

Cześć :)

Mam taki problem że mam funkcję w C# która jako parametr przyjmuje string i potem go modyfikuje i go zwraca. Tę funkcję następnie eksportuję przy użyciu biblioteki UnamnagedExport ponieważ chciałbym ją wywołać z poziomu skryptu InnoSetup . Skrypt się kompiluje ale jak go uruchamiam to dostaję komunikat błędu w momencie w którym wywołuję funkcję z biblioteki.
Moja funkcja w C# wygląda tak jak poniżej:

[DllExport(ExportName = "EncryptText", CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.LPWStr)]
        [ComVisible(true)]
        public static string EncryptText([MarshalAs(UnmanagedType.LPWStr)] string plainText)
        {
           
        }

Próbowałem używać różnych typów zarówno po stronie C# jak i InnoSetup ale za każdym razem dostaję ten sam błąd. Z tego co wyczytałem w internecie to właśnie ze zwracaniem typu string jest problem.W załączniku przesyłam screena z błędem jaki otrzymuję. Czy ktoś z was miał podobny problem?

0

Spróbuj po stronie InnoSetup użyć PChar, a po stronie C# - StringBuilder. Aha, to chyba jest w standardzie, ale może też jawnie ustaw kodowanie na UTF-8 albo UTF-16 (w sygnaturze funkcji)

0

Zrobiłem tak jak mówisz ale po użyciu typu PChar w InnoSetup skrypt się nie kompiluje i dostaję komunikat błędu o treści Unknown Type.

0

Zmieniłem typ po stronie InnoSetup na WideString a po stronie C# użyłem BStr. Poniżej podaję jak aktualnie wygląda funkcja w c# a załączniku przekazuję komunikat tym razem o innej treści.

[DllExport(ExportName = "EncryptText", CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.BStr)]
        [ComVisible(true)]
        public static string EncryptText([MarshalAs(UnmanagedType.BStr)] string plainText)
        {
            
        }
1

Po stronie InnoSetup spróbuj w takim razie PWideChar.

Wyjaśnienie.
string w InnoSetup, Delphi, C++, czy czymś jeszcze innym to zupełnie inny typ danych. Stringi w językach programowania nie są ze sobą tożsame. W różnych językach zawierają różne informacje w różnych miejscach. Tzn. string to tak naprawdę taki typ pomocniczy na czysty łańcuch znaków.

Podstawowym typem, który trzyma dane tekstowe jest tablica znaków. Czyli ciągły obszar w pamięci zakończony znakiem NUL, np:

(to jest fragment pamięci - jeden znak to jeden bajt, dla uproszczenia)

ALA MA KOTA[\0]

gdzie [\0] to jeden znak - NUL (ASCII = 0)

To jest tablica znaków, która reprezentuje jakiś ciąg tekstowy. Tablica ma swój początek w pamięci i to jest tzw. adres tablicy. Nikt nie zna jej długości. Po prostu odczytywane jest wszystko aż do napotkania znaku NUL.

Następnie mamy coś takiego jak string. W każdym języku wygląda on inaczej. Np. w Delphi w pamięci MOŻE wyglądać tak:

(fragment pamięci, każdy znak to jeden bajt, określający liczbę. Dla ułatwienia napisałem po ludzku w hexie)

5AFCD

I co to jest? Jak to się ma do Ali, która ma kota? Ano string jest typem referencyjnym. Tzn., że odnosi się gdzieś do sterty. Wskazuje. Jest tak jakby wskaźnikiem na jakiś obszar na stercie.
Tutaj konkretnie wskazujemy na adres 0x5AFCD.

A co mamy pod tym adresem? Może być np. coś takiego:
(fragment w pamięci <-- tutaj pierwsze 8 znaków to tak naprawdę zapis binarny liczb, dla ułatwienia napisałem je po ludzku w hexie)

0001000BALA MA KOTA[\0]

Ten string ma w sobie więcej informacji, pierwsze 4 bajty to może być reference count, kolejne 4 bajty to mogą być długością stringa, a następnie dopiero masz faktyczny ciąg znaków.
I teraz, gdy kompilator zobaczy, że ma do czyniena z typem string, dokładnie wie, co ma zrobić.

Ty przekazując jakieś dane pomiędzy bibliotekami, tak naprawdę przekazujesz ciąg bajtów.
A więc przekazując stringa, przekazujesz tak naprawdę "wskaźnik", który wskazuje gdzieś na stertę. Musisz zatem przekazać coś, o czym obie strony doskonale są poinformowane. Tym czymś jest np. stara, dobra tablica znaków zakończona znakiem NUL.

A więc ze strony C# musisz przekazać taką tablicę (C# to robi jakoś automagicznie, więc może wystarczy jeśli faktycznie przekażesz tam string. Jeśli INNOSetup ma to tylko ODCZYTAĆ i nic z tym więcej nie zrobić). Po stronie InnoSetup jednak musisz już użyć tablicy znaków. Normalnie w Delphi to jest PChar (to jest unikodowa tablica znaków), a w InnoSetup gdzieś znalazłem info, że nie ma PChar, ale jest PAnsiChar i PWideChar. Różnią się tylko tym, że PAnsiChar to jest tablica znaków ANSI (czyli każdy znak zajmuje jeden bajt), a PWideChar to jest unikodowa tablica znaków.

0

@Juhas: Dziękuję za pomoc i wyjaśnienie problemu.
Po stronie InnoSetup użyłem typu PAnsiChar, ponieważ typ który proponowałeś - PWideChar według InnoSetup nie istnieje. Pobrałem jeszcze dla pewności najnowszą wersję InnoSetup ale tam też kompilator twierdzi że taki typ nie istnieje więc pozostałem przy typie PAnsiChar. Poniżej wklejam kod funkcji po stronie C# jakby ktoś miał kiedyś podobny problem.

[DllExport(ExportName = "EncryptText", CallingConvention = CallingConvention.StdCall)]
        [return: MarshalAs(UnmanagedType.LPStr)]
        [ComVisible(true)]
        public static string EncryptText([MarshalAs(UnmanagedType.LPStr)] string plainText)

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