wskaźnik przekazany do C?

0

Ok więc mam następujący problem.

Tworzę plik dll w którym mam kilka metod. Jedna z nich tworzy tablicę z wynikami.
Osoba dla której to piszę programuje w C i u siebie chce się odwołać do tej biblioteki, dostałem pytanie czy mógłbym później przekazać jako wynik metody wskaźnik do stworzonej wcześniej tablicy (zamiast przekazywać całą tablicę).

Starałem się coś znaleźć na ten temat, ale nie mam pomysłu jak to zrobić :/

Będę wdzięczny za każdą pomoc.

0

Tablicy jako elementu na stercie zarządzanej - nie. Zainteresuj się: C++/CLI oraz marshallingiem bądź technologią COM.

0

Tablicy jako elementu na stercie zarządzanej - nie.

Ok, a jakiś pomysł jak to najlepiej/najwydajniej rozwiązać?
Scenariusz mam dość prosty: W dll muszę połączyć się z bazą i zapisać wyniki do tablicy czy jakiegoś innego obiektu i dać dostęp do tego tak, żeby można było to odczytać w C.

Zainteresuj się: C++/CLI oraz marshallingiem bądź technologią COM.

Niestety z C++ nie mam za wiele doświadczeń, poza tym to leży bardziej po stronie tej drugiej osoby z którą współpracuje. Niestety ani on nie zna C#, ani ja nie znam C. Tak więc szukamy jakiegoś rozwiązania.

0

Rozwiązanie też jest proste i właśnie ci je podałem. Nawet dwa. Albo ty albo ta druga osoba napisze wrapper w C++/CLI albo wyeksportujesz swoją bibliotekę jako COM.
Jeżeli chcesz opakować jedną funkcję to wrapper będzie miał kilkanaście linijek.

0

Rozwiązanie też jest proste i właśnie ci je podałem. Nawet dwa. Albo ty albo ta druga osoba napisze wrapper w C++/CLI albo wyeksportujesz swoją bibliotekę jako COM.
Jeżeli chcesz opakować jedną funkcję to wrapper będzie miał kilkanaście linijek.

Hmm nie wiem czy się dobrze rozumiemy (chociaż bardziej obstawiałbym, że ja nie wiem o co chodzi).
Aktualnie używam takiego rozwiązania:
http://www.codeproject.com/Articles/16310/How-to-Automate-Exporting-NET-Function-to-Unmanage
i import Dll'a działa super i z tym nie ma problemu, nawet testowo robiliśmy tak, że on wywoływał z C metodę która zwracała tablicę dwuwymiarową i odczytywał te wyniki. Problem natomiast jest taki, że wiemy, iż te dane mogą być dużo większe i po tym jak ja zalokuję pamięć dla tablicy on będzie musiał zrobić to samo u siebie, zastanawialiśmy się czy jakoś da się to obejść i właśnie np. przekazać mu wskaźnik do tej tablicy aby on nie musiał już tworzyć drugiej identycznej.

1

Wyeksportuj sobie taką metodę w takiej postaci:

public unsafe static int* MyMethod()
{
    Marshal.AllocHGlobal(sizeof(int) * 1000);
    int* ptr = (int*)ret.ToPointer();

    for (int i = 0; i < 1000; i++)
        ptr[i] = 255;

    return ptr;
}

Pamięć można (trzeba ;)!) zwolnić za pomocą LocalFree (http://msdn.microsoft.com/en-us/library/aa366730.aspx) po stronie C bądź Marshal.FreeHGlobal po stronie .NET.

0

witam po długim weekendzie.
Przede wszystkim dziękuję za już udzieloną pomoc. Popatrzyłem sobie na tą metodę i zauważyłem, że nie ma tam deklaracji "ret". Znalazłem iż należy zadeklarować go jako "IntPtr". I teraz mam kilka dalszych pytań może już bezpośrednio na kodzie:

unsafe public static int* MyMethod(IntPtr ret)
        {
            ret = Marshal.AllocHGlobal(sizeof(int) * 1000);
            /* 1. Pytanie: Tutaj alokuję pamięć dla mojej tablicy. Mam tablicę dwuwymiarową [X,6]
             * więc delkaracja jak mniemam powinna wyglądać tak:
             * ret = Marshal.AllocHGlobal(sizeof(double) * 6 * {liczba_elementow_w_tablicy} ); */

            int* ptr = (int*)ret.ToPointer();
            // 2. Tutaj powinna być zamiana na double więc:
            // double* ptr = (double*)ret.ToPointer();

            for (int i = 0; i < 1000; i++) ptr[i] = 255;
            //3. Tutaj rozumiem że przechodzisz po wszystkich elementach i ustawiasz wskaźnik na każdy z nich... więc u mnie powinno być mniej więcej:
            // for (int i = 0; i < {liczba_elementow}; i++) ptr[i] = 255;
            // Nie jestem tylko pewien jak zrobić, aby dobrze przypisać to dla tablicy dwuwymiarowej... 
            
            return ptr;
        }

Byłbym wdzięczny gdybyś mógł jeszcze raz rozwiać moje wątpliwości :) (lub ktokolwiek inny w temacie)

0

ale po co ci to ret jako parametr? zrób zmienną lokalną w funkcji.
po stronie C# tablicę możesz traktować jako double[X*6], a po stronie C double[X][6] czy tam double[6][X]

0
Azarien napisał(a):

ale po co ci to ret jako parametr? zrób zmienną lokalną w funkcji.
po stronie C# tablicę możesz traktować jako double[X*6], a po stronie C double[X][6] czy tam double[6][X]

'ret' chciałem zrobić jako parametr po to abym mógł się do niej odwoływać jak do zmiennej globalnej i móc później np. zwolnić pamięć z poziomu c#

0
  1. ret jest liczbowo tym samym co ptr, i możesz dowolną ilość razy rzutować z IntPtr na wskaźnik i z powrotem
  2. popełniasz bardzo podstawowy błąd:
void foo(int x)
{
  x = 3;
}

czy zmienna x zachowa zmienioną wartość po wyjściu z funkcji?

0

ok rozumiem o co chodziło...
w każdym razie nadal mam pytanie

unsafe public static int* MyMethod()
{
    int liczba_elementow = tabWyniki.Length; //1

    IntPtr ret = Marshal.AllocHGlobal(sizeof(double) * liczba_elementow); //2
    int* ptr = (int*)ret.ToPointer(); //3

    for (int i = 0; i < liczba_elementow; i++) ptr[i] = 255; //4

    return ptr;
}

Zmodyfikowałem kod na powyższy, ale nie jestem pewien nadal niektórych rzeczy więc chciałem Was prosić o pomoc czy dobrze rozumiem powyższy kod
//1 - tutaj z mojej tablicy dwuwymiarowej (tabWyniki) pobieram liczbę elementów
//2 - alokuję pamięć... w tym przykładzie dla double, a co jeżeli w kroku 2 mam nie liczby ale stringi?
//3 - tutaj ustawiam wskaznik do tej zalokowanej postaci
//4 - tutaj nastepuje dopiero przypisanie wartosci do wszystkich elementow

0

w tym przykładzie dla double, a co jeżeli w kroku 2 mam nie liczby ale stringi?
Marshal.StringToHGlobalAnsi jeśli chcesz pod C mieć char* albo Marshal.StringToHGlobalUni jeśli chcesz string w unikodzie —wchar_t*.
Zwalniamy tak samo: Marshal.FreeHGlobal.

Do kopiowania danych z tablicy też lepiej użyć Marshal.Copy niż w pętli przepisywać elementy.

Ogólnie C# nie jest dobrym językiem do takich rzeczy. Już samo to że eksportujesz funkcje z DLLki to jest hack. Bez kombinowania można to zrobić pod C++/CLI. Nie trzeba byłoby też robić ręcznie żadnych Alloc czy Free, bo są gotowe funkcje do konwersji stringów i tablic.

0

PS. pamiętaj że IntPtr to jest wskaźnik. Jeśli chcesz tablicę stringów, zaalokuj najpierw tablicę i wypełnij ją wskaźnikami które zwraca StringToHGlobalAnsi.
Przy zwalnianiu najpierw w pętli zwolnij stringi, potem całą tablicę.

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