Ładowanie dynamiczne DLL ze strukturami użytkownika

0

Witam wszystkich
Chciałbym załadować DLL zewnętrzny, w którym jest przykładowa klasa i struktura:

namespace bibliotekaTestowaDoZaladowania
{
  public struct S1
  {
    public Point3d p1;
  }

  public static class klasaTestowa
  {
    public static Point3d metoda1( S1 s )
    {
      return s.p1;
    }
  }
}

przy czym:

Point3d

to struktura dostępna publicznie. Jak to załadować, aby móc utworzyć argument typu S1, ustawić jego zawartość i przekazać do metody?

0

Przez dynamiczny rozumiesz DLL ładowany w trakcie wykonywania? Czy na pewno nie możesz
dodać go jako referencji do kompilacji i używać normalnie poprzez using? Jeśli nie, to możesz dynamicznie generować kod np. za pomocą Roslyn Scripting albo CodeDom, ale to niebezpieczna droga.

0

Chodzi o to, że mam tę klasę w zupełnie innym rozwiązaniu. Nie chcę z różnych przyczyn dołączać jej do bieżącego rozwiązania, ma być w formie gotowego DLL. Ma być wczytywana przykładowo w formie:

        byte[] daneWe = File.ReadAllBytes( sciezkaDll );
        Assembly DLL = Assembly.Load( daneWe );
        Type t = DLL.GetType( "bibliotekaTestowaDoZaladowania.klasaTestowa" );
        Object[] argKonstr = <coś tam>;
        Object obiektKlasy = Activator.CreateInstance( t, argKonstr );
        MethodInfo metoda = t.GetMethod( "metoda1" );
        Object[] argMet = { tu jest problem };
        bool powodzenie = ( bool )metoda.Invoke( obiektKlasy, argMet );

Opcjonalnie dopuszczam skopiowanie definicji struktur użytkownika do bieżącego rozwiązania, żeby dało się utworzyć z nich argumenty funkcji. Wolałbym jednak tego uniknąć.

0

Dopuszczam jeszcze jedną zmianę - żeby w strukturze S1 był konstruktor z argumentem (argumentami) inicjującymi zawartość struktury:

  public struct S1
  {
    public Point3d p1;
    public S1( Point3d p )
    {
      p1 = p;
    }
  }
0

Dobra, chyba udało się. Zawartość zmieniona klasy ładowanej. Musiałem zmienić klasę ze statycznej na normalną:

namespace bibliotekaTestowaDoZaladowania
{
  public struct S1
  {
    public Point3d p1;
    public S1( Point3d p )
    {
      p1 = p;
    }
  }

  public class klasaTestowa
  {
    public Point3d metoda1( S1 s )
    {
      return s.p1;
    }
  }
}

Wywołanie:

byte[] daneWe = File.ReadAllBytes( sciezkaDll );
Assembly DLL = Assembly.Load( daneWe );
Type tKlasy = DLL.GetType( "bibliotekaTestowaDoZaladowania.klasaTestowa" );
Object obiektKlasy = Activator.CreateInstance( tKlasy );
MethodInfo metoda = tKlasy.GetMethod( "metoda1" );
Type tStruktury = DLL.GetType( "bibliotekaTestowaDoZaladowania.S1" );
Object[] argKonstrStrukt = { new Point3d( 1, 2, 3 ) };
Object obiektStruktury = Activator.CreateInstance( tStruktury, argKonstrStrukt );
Object[] argMet = { obiektStruktury };
Point3d p = ( Point3d )metoda.Invoke( obiektKlasy, argMet );

Celowo wczytuję najpierw do tablicy byte[], żeby można było poddać zawartość pliku wstępnej obróbce, na przykład dekompresji.
Człowiek się uczy całe życie...

0

Nie trzeba zmieniać klasy statycznej na niestatyczną. Po prostu jak masz klasę statyczną, to nie twórz jej instancji, bo to jest przecież niemożliwe.

Type tKlasy = DLL.GetType("bibliotekaTestowaDoZaladowania.klasaTestowa");
MethodInfo metoda = tKlasy.GetMethod("metoda1", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
Point3d p = (Point3d) metoda.Invoke(null, obiektStruktury);

PS. Czemu stawiasz spacje wewnątrz nawiasów?

0

@somekind: Dzięki. Spacje w nawiasach to taki nawyk od kilkunastu lat, gdy jeszcze trochę pisałem amatorsko w C++. Szukałem wtedy informacji jak formatować kod i jakoś tak wyszło... Treść nawiasu jest jakby lepiej widoczna i nawias nie zaciemnia jej.
A teraz jeszcze jedno pytanie: Jeśli mam klasę (nawiążę do tego, że nie jest statyczna):

namespace bibliotekaTestowaDoZaladowania
{
  public struct S1
  {
    public Point3d p1;
    public S1( Point3d p )
    {
      p1 = p;
    }
  }

  public class klasaTestowa
  {
    public Point3d metoda1( S1 s )
    {
      return s.p1;
    }
    public S1 metoda2( S1 s, double mnoznik )
    {
      return new S1( new Point3d( s.p1.X * mnoznik, s.p1.Y * mnoznik, s.p1.Z * mnoznik ) );
    }
  }
}

Chciałbym zrzutować wynik metody metoda2 tak, abym mógł pobrać jej składowe. Jak to mogę zrobić?

0

@badziewiak: Z ciekawości zapytam: co cię ogranicza, że musisz ładować plik DLL przez refleksję, a nie możesz po prostu skopiować go do swojego projektu i dodać jako referencję? Jeśli to nie tajemnica, po prostu mnie zaciekawiło.

0

@maszrum: To co pokazuję to abstrakcyjny przykład, który ma być prosty dla jasnego przedstawienia idei. W rzeczywistości niektóre dll są u mnie rozwijane w innych rozwiązaniach. Nie chcę ich łączyć ani kopiować kodu projektu. Traktuję to, że mam gotowy dll i nie wnikam w jego strukturę wewnętrzną. Wczytywany dll żyje sobie swoim własnym życiem, a ja w konkretnym rozwiązaniu chcę go wczytać i z niego skorzystać.

1

@badziewiak: Pomyśl nad tworzeniem z tych DLL-ek pakietów nugetowych, które będziesz mógł sobie pobierać i aktualizować według potrzeb. Nie musisz ich nawet wysyłać na jakiś serwer. W Visual Studio możesz zdefiniować katalog w którym będziesz je sobie przechowywał, a menadżer pakietów się zajmie resztą.

0

@maszrum: O tym też pomyślę, ale na chwilę obecną potrzebuję czegoś takiego. Czy ktoś może zna odpowiedź na pytanie zadane w https://4programmers.net/Forum/C_i_.NET/350612-ladowanie_dynamiczne_dll_ze_strukturami_uzytkownika?p=1755534#id1755534 ?

0
somekind napisał(a):

Nie trzeba zmieniać klasy statycznej na niestatyczną. Po prostu jak masz klasę statyczną, to nie twórz jej instancji, bo to jest przecież niemożliwe.

Type tKlasy = DLL.GetType("bibliotekaTestowaDoZaladowania.klasaTestowa");
MethodInfo metoda = tKlasy.GetMethod("metoda1", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public);
Point3d p = (Point3d) metoda.Invoke(null, obiektStruktury);

Bardzo dziękuję za cenną wskazówkę. Pro forma - powinno być:

 Point3d p = (Point3d) metoda.Invoke(null, new object[] { obiektStruktury1 });

ale to pewnie dlatego, że z głowy pisałeś. W takiej formie działa.

0
badziewiak napisał(a):

Chciałbym zrzutować wynik metody metoda2 tak, abym mógł pobrać jej składowe. Jak to mogę zrobić?

badziewiak napisał(a):

@maszrum: O tym też pomyślę, ale na chwilę obecną potrzebuję czegoś takiego. Czy ktoś może zna odpowiedź na pytanie zadane w https://4programmers.net/Forum/C_i_.NET/350612-ladowanie_dynamiczne_dll_ze_strukturami_uzytkownika?p=1755534#id1755534 ?

Wyniku nie zrzutujesz na S1. Możesz co najwyżej przez refleksję dostać się do pola Point3d p1 obiektu typu S1.

MethodInfo metoda = tKlasy.GetMethod("metoda2");
Object[] argMet = { obiektStruktury, 0.5 };
Object p = metoda.Invoke(obiektKlasy, argMet);
FieldInfo p1FieldInfo = tStruktury.GetField("p1");
Point3d p1 = (Point3d) p1FieldInfo.GetValue(p);
Console.WriteLine($"{p1.X}, {p1.Y}, {p1.Z}");

Według mnie niepotrzebnie pchasz się w tę refleksję. Sprawi ci to wiele problemów.

0

@maszrum: Ślicznie dziękuję, to mi w zupełności wystarczy. Czyli całość by wyglądała tak:
Klasa statyczna:

namespace bibliotekaTestowaDoZaladowania
{
  public struct S1
  {
    public Point3d p1;
    public S1( Point3d p )
    {
      p1 = p;
    }
  }

  public static class klasaTestowa
  {
    public static Point3d metoda1( S1 s )
    {
      return s.p1;
    }
    public static S1 metoda2( S1 s, double mnoznik )
    {
      return new S1( new Point3d( s.p1.X * mnoznik, s.p1.Y * mnoznik, s.p1.Z * mnoznik ) );
    }
  }
}

kod wywołujący obydwie metody:

        byte[] daneWe = File.ReadAllBytes( sciezkaDll );
        Assembly DLL = Assembly.Load( daneWe );
        Type tKlasy = DLL.GetType( "bibliotekaTestowaDoZaladowania.klasaTestowa" );
        Type tStruktury = DLL.GetType( "bibliotekaTestowaDoZaladowania.S1" );
        Object[] argKonstrStrukt1 = { new Point3d( 1, 2, 3 ) };
        Object obiektStruktury1 = Activator.CreateInstance( tStruktury, argKonstrStrukt1 );
        Object[] argMet1 = { obiektStruktury1 };
        MethodInfo metoda1 = tKlasy.GetMethod( "metoda1", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public );
        Point3d p = ( Point3d )metoda1.Invoke( null, new object[] { obiektStruktury1 } );

        MethodInfo metoda2 = tKlasy.GetMethod( "metoda2" );
        Object[] argMet2 = { obiektStruktury1, 0.5 };
        Object s = metoda2.Invoke( null, argMet2 );
        FieldInfo p1FieldInfo = tStruktury.GetField( "p1" );
        Point3d p1 = ( Point3d )p1FieldInfo.GetValue( s );
0

Witam ponownie. Mam jeszcze pytanie: Czy istnieje jakiś sposób na to, aby do dll załadowanego przez refleksję ustawić folder, w którym może sobie znaleźć pozostałe pliki dll podrzędne?

0

Witam
Wracam do tematu. Powiedzmy, że z danych projektów zebrałem wszystkie zależności utworzone w GUI VS przez References i są zapisane w pliku. Mam zrobioną swoją klasę, która te zależności zbiera z pliku i na podstawie nazwy binarki tworzy listę innych binarek, które trzeba wczytać we właściwej kolejności (od najbardziej zagnieżdżonych). Pytanie:
Czy da się wczytać te binarki (tylko do tablicy byte[]), aby binarki wyżej były w stanie odnaleźć to, co się wczyta?

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