DLL C++

Jeśli masz coś do powiedzenia w sprawie LabVIEW napisz. Tutaj są tematy, których nie można uściślić do innych działów.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: Dziech »

Witam,

Mam gotową bibliotekę DLL. Potrzebuję dostać się do konstruktora, ale labview nie widzi klas cpp niestety. Wyczytałem gdzieś, że można zrobić "wrapper" z cpp do c. Tylko nie za bardzo się na tym znam i nigdy tego nie robiłem. Może na forum jest ktoś po takim zabiegu i by się wypowiedział jak to zrobić ? : > Podobny wątek powinienem wrzucić chyba na jakieś forum cpp - skopiuję odpowiedź jak dostanę - dla potomnych : )

pozdrawiam,
D
oczekp
Posty: 161
Rejestracja: 22 lis 2009 15:12
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: oczekp »

Hej, tu masz bardzo dobry link do tego jak takiego wrappera utworzyć:
http://labviewwiki.org/DLL/shared_library

Cytując:
Jak wiadomo w c++ struktura to też jest klasa, zatem, aby utworzyć klasy nieprzezroczyste(niewidoczne), należy po prostu typedefingować (przypisać nazwę alternatywną) klasę do struktury o tej samej nazwie.
A następnie trzeba by wywoływały się funkcje w C zamiast C++. Dokonuje się tego przez dyrektywę extern "C".

Postaram się zaraz utworzyć jakiś prosty przykładzik i wrzucić go tu.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: Dziech »

Super ! : ) Dziękuję za link. Przeszukałem Internet, ale nie trafiłem na tą stronę. Jak zrobię swoje to też postaram się coś tutaj wklepać : )

Dzięki,
D. : )
oczekp
Posty: 161
Rejestracja: 22 lis 2009 15:12
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: oczekp »

Zgodnie z obietnicą podaje przykład, może się komuś przyda.
Załóżmy, że mamy do dyspozycji klasę punkt zdefiniowaną w pliku punkt.h
o takiej budowie:

Kod: Zaznacz cały

class punkt
{
public:
	punkt(int,int); //konstruktor
	virtual ~punkt(); //destruktor
	void przesun(int,int); //funkcja przesuwająca punkt x,y o zadaną wartość
	void pokaz(int*,int*); //wyświetla aktualne wartości x,y
private:
	int x,y; //współrzędne punktu x,y
};
oraz plik punkt.cpp :

Kod: Zaznacz cały

#include "punkt.h"
#include <windows.h>

punkt::punkt(int a, int b)
{                
  x=a;
  y=b;     
}
punkt::~punkt()
{
//destruktor
}
void punkt::przesun(int a, int b)
{
x+=a;
y+=b;
}
void punkt::pokaz(int* a,int* b)
{
*a=x;
*b=y;
}
Załóżmy, że chcemy skorzystać sobie z klas napisanych w C++ i utworzyć plik dll do wywoływania różnych funkcji danej klasy w LabVIEW. Jak wiadomo w LV można wywoływać jedynie DLL napisane w czystym C, zatem należy utworzyć opakowanie (wrapper), które "przetłumaczy" klasę w C++ na zwykłe wywołania funkcji w C. Dokładniej oznacza to, że musimy napisać jakiś kod w C, który LabVIEW zrozumie i pośrednio wywoła żądaną klasę w C ++.
Aby tego dokonać należy napisać specjalne opakowanie (wrapper). Jak wiadomo w C++ struktura jest klasą, zatem podczas deklarowania w nagłówku wrappera można napisać coś takiego:

Kod: Zaznacz cały

typedef struct punkt punkt;
powodując tym samym, że klasa będzie nieprzezroczysta dla naszego wrappera.
Następnie tworzymy funkcje, które będziemy chcieli sobie wywoływać w LabVIEW, a przed całością dajemy specyfikator extern "C" . Przykładowo nagłówek takiego wrappera może wyglądać tak:

Kod: Zaznacz cały

]#ifndef _DLL_H_
#define _DLL_H_

#if BUILDING_DLL
# define DLLIMPORT __declspec (dllexport)
#else /* Not BUILDING_DLL */
# define DLLIMPORT __declspec (dllimport)
#endif /* Not BUILDING_DLL */

#ifdef __cplusplus
   extern "C" { /* using a C++ compiler */
#endif

typedef struct punkt punkt; /* sprawia, że klasa jest nieprzezroczysta dla wrappera */
       
       DLLIMPORT punkt* konstruktor(int a, int b);
       DLLIMPORT void przesun_xy(punkt* LV_ref, int a, int b);
       DLLIMPORT void pokaz_xy(punkt* LV_ref, int* a, int* b);
       DLLIMPORT void destruktor(punkt* LV_ref);
       
#ifdef __cplusplus
   }
#endif

#endif /* _DLL_H_ */
Zaś same funkcje w pliku głównym:

Kod: Zaznacz cały

DLLIMPORT punkt* konstruktor(int a, int b)
{
          return new punkt(a,b);
}

DLLIMPORT void przesun_xy(punkt* LV_ref, int a, int b)
{
          LV_ref->przesun(a,b);
}

DLLIMPORT void pokaz_xy(punkt* LV_ref, int* a, int* b)
{
          LV_ref->pokaz(a,b);
}

DLLIMPORT void destruktor(punkt* LV_ref)
{
          delete LV_ref;
}
Mając wykonany taki dll możemy zabrać się za wstawienie go do LabVIEW, przykład obrazu w załączniku.

Ogólnie polecam przeczytanie strony co wcześniej została podana i jeszcze tej:
http://forums.devx.com/showthread.php?t=170263

Tekst pisałem i tłumaczyłem na szybko więc może mieć trochę błędów.

W załączniku wrzucam przykład takiej biblioteki dll -> punkt.dll
nagłówek wrappera dll.h
plik wrappera dllmain.cpp
nagłówek klasy punkt.h
plik klasy punkt.cpp
oraz przykładowe vi, które korzysta z napisanego dlla -> punkt.vi
Załączniki
punkt.zip
(25.97 KiB) Pobrany 277 razy
Przykład wywołania konstruktora, funkcji przesun, pokaz oraz destruktora
Przykład wywołania konstruktora, funkcji przesun, pokaz oraz destruktora
Ostatnio zmieniony 20 sie 2011 20:17 przez oczekp, łącznie zmieniany 1 raz.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: Dziech »

Dziękuję ! : ) Na szczęście zrozumiałem całą ideę ze strony, którą podałeś wcześniej : )

Leci pochwała i dzięki piękne za rozbudowany przykład!

D.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: Dziech »

Hm, a jeśli mam coś takiego w pliku nagłówkowym :

Kod: Zaznacz cały

void virtual apply() = 0;

Poza tym, że moje umiejętności cpp trochę zardzewiały i nie umiem dojść co znaczy ta składnia ; > to nie znalazłem jak można to zmienić. Trochę ciężko się pracuje, jak się nie zna do końca całego kodu : >
EDIT:
A jeszcze tak - może się to komuś przyda - jeśli chcę użyć w lv tylko niektórych funkcji, bo nie potrzebnie przerabiam taką ilość kodu! Muszę umieścić je w extern C, a resztę wywalić poza ? Przepraszam jeśli gdzieś jest odpowiedź : >

D.
Ostatnio zmieniony 16 sie 2011 12:01 przez Dziech, łącznie zmieniany 1 raz.
oczekp
Posty: 161
Rejestracja: 22 lis 2009 15:12
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: oczekp »

W extern C tworzysz sobie tylko te funkcje, które chcesz sobie wyłuskać z klasy, to znaczy, tylko to co chcesz, resztę po prostu zostawiasz w spokoju:)
void virtual apply() = 0; jest to czysto wirtualna funkcja składowa zdefiniowana w klasie abstrakcyjnej. Więcej, np. tu:
http://www.lanform.com.pl/cpptxt/l36.txt
Zazwyczaj klasy abstrakcyjne to klasy bazowe, w linku co przesłałem wcześniej jest przykład odwoływania się do funkcji wirtualnych w klasach dziedziczonych.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: Dziech »

Witam,

dziękuję za pomoc - działa DLL, ale mam problem z powiązaniem do Labview. Chodzi o to, że mam taki układ - prawdopodobnie można to jakoś zdesignować inaczej, żeby działało, ale nie mam pojęcia jak.


Chodzi o to, że przy danych case'ach nie chcę wywoływać niektórych funkcji DLL. Trzeba osobne DLL stworzyć, czy może wsadzić DLL do każdego case'a ? Nie da się pozostawić otwartego node'a.

D.

P.S. Po dodaniu default if unwired otrzymuję błąd nr 12 z DLL node'a

Possible reason(s):

LabVIEW: Some system capacity necessary for operation is not enabled.
Załączniki
Układ
Układ
Ostatnio zmieniony 17 sie 2011 15:05 przez Dziech, łącznie zmieniany 2 razy.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: Dziech »

Myślałem, że to pomoże, ale teraz każda DLL node daje ten sam błąd. Poza tym, jak usunąłem całą treść funkcji, to wyskakuje ten sam błąd. Więc to raczej nie wina kodu.

P.S Nie - nie kompilowałem DLL dla 64 bit.
Załączniki
układ 2
układ 2
Ostatnio zmieniony 17 sie 2011 21:26 przez Dziech, łącznie zmieniany 1 raz.
oczekp
Posty: 161
Rejestracja: 22 lis 2009 15:12
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: oczekp »

ciężko tak powiedzieć na pierwszy rzut oka. Najlepiej jakbyś mógł wrzucić cały kod to się wtedy szybciej dojdzie, gdzie jest błąd.
Z pierwszych uwag, to na pewno jak przesyłasz tablicę o n elementach to zwyczajem w programowaniu c jest przesłanie rozmiaru tablicy. Bo za pewne jedziesz w dll jakimś forem.
(To są te same dll i tylko odwołujesz się w nich do innej funkcji? Czy tej samej?)

Ja obstawiam na 100 coś z dll-em, bo drugi załącznik wygląda okej.
Co do pierwszego to muszą być podpięte wszystkie wejścia. Nie wiem dokładnie co tam się dzieje wewnątrz dll-a, ale zawsze możesz podpiąć pustą tablicę jak nie będziesz korzystać, w kodzie c++ sobie sprawdzić czy pusta i wtedy nie robić, co tam byś chciał, albo zrobić jeszcze jakieś wejście typu boolean i w zależności od true czy false wywoływać inną funkcją, bądź jeszcze jakieś inne kombinacje. W każdym bądź razie, jeżeli w definicji funkcji jest, że trzeba podpiąć wszystkie, to muszą być:)
Ja bym na Twoim miejscu jednak zrobił to tak, że dla każdego case dałbym tego samego dlla, z tym, że dla każdego stanu wywoływał odpowiednią funkcję. No chyba, że są jakieś fajniejsze i mądrzejsze rozwiązania.:)

PS> Czyli używasz LV 32bitowe z 32 bitowymi dllkami?:)
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: Dziech »

oczekp pisze: Ja bym na Twoim miejscu jednak zrobił to tak, że dla każdego case dałbym tego samego dlla, z tym, że dla każdego stanu wywoływał odpowiednią funkcję. No chyba, że są jakieś fajniejsze i mądrzejsze rozwiązania.:)

PS> Czyli używasz LV 32bitowe z 32 bitowymi dllkami?:)
Dokładnie tak zrobiłem. Wywołuję w każdym dll node inne funkcje. Po prostu argumenty są tego samego typu i tak to wygląda. No ok - poza dwoma case'ami. W tych dwóch caseach używam tej samej funkcji, ale wciąż jest to inny dll node.

Nie jestem pewny co rozumiesz przez używanie LV 32 bitowego. Zmienia się gdzieś typ node'a, czy chodzi Ci o mój system ? : > DLL są 32b.

Okazało się, że nie potrzebuję dostępu do obiektów, więc po prostu napisałem swoje funkcje. Być może o czymś zapomniałem ? Nie jest mi też potrzebne definiowanie _cplusplus, bo kompilator VS i tak kompiluje cpp.

Kod: Zaznacz cały

#define DLLIMPORT __declspec (dllexport)



[moje include ""]
#include <sstream>
#include <stdexcept>
#include <memory>

using namespace std;
using namespace adocol;



ODev* device2;
CDev* cdev;
ODev* device1;


extern "C" {
	


DLLIMPORT void ReadDMC(const char* fileName)
{
\\początek [...] - oznacza, że wyciąłem kod ze względów estetycznych

	auto_ptr<Data> newdata;
	try {
		newdata = auto_ptr<Data>(new Data(string(fileName))); }
	catch [...]
	try{
		device1 = newdata->createOutput(); }
	[...]
}

DLLIMPORT	void setVoltages(const double* vlt) {
		device1->set(vlt);
	}


DLLIMPORT	void setZernike(const double* zer) {
		device2->set(zer);
		cdev->sweep();
	}


	BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
			{
         delete device1;
		 delete device2;
		 delete cdev;
		 device1 = NULL;
		 device2 = NULL;
		 cdev = NULL;
			}
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
	}

}
Acha - i jeszcze linkuje DLL z funkcjami, których tutaj nie definiuję. Działają na pewno i nie mam do nich dostępu niestety, żeby upublicznić. Na sto procent to nie jest problem.
Ostatnio zmieniony 17 sie 2011 23:58 przez Dziech, łącznie zmieniany 1 raz.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: Dziech »

Wydaje mi się, że to coś z ścieżkami... Jak usunąłem określanie ścieżki do DLL z Block Diagram, LV zaczął przeglądać dysk twardy w poszukiwaniu DLL. Dałem przeglądaj i wskazałem DLL. W konfiguracji node'a widniała dalej nazwa DLL. Po odpaleniu nie daje błędu. To o node z stringiem.

Natomiast node'y w caseach po zrobieniu tego samego, dają błąd :


Error 1097 occurred at Call Library Function Node in mirrorcontrol.vi

LabVIEW: An exception occurred within the external code called by a Call Library Function Node. The exception might have corrupted the LabVIEW memory. Save any work to a new location and restart LabVIEW.
Ostatnio zmieniony 18 sie 2011 20:14 przez Dziech, łącznie zmieniany 2 razy.
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

DLL C++

Post autor: Dziech »

Wstawiłem

Kod: Zaznacz cały

if(deviceX)
jako warunek wykonania każdej funkcji (orpócz oczywiście readDMC) i DLL działa. Pomijając fakt, że działa tylko jak określę ścieżkę w konfiguracji a nie przez nazwę i kontrolkę Current VI's path...

Czyli problem jest w tworzeniu obiektów ODev i CDev (wygląda na to, że są NULL nawet po utworzeniu). Co to może być ? : /
Ostatnio zmieniony 19 sie 2011 11:38 przez Dziech, łącznie zmieniany 1 raz.
oczekp
Posty: 161
Rejestracja: 22 lis 2009 15:12
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: oczekp »

Pomijając fakt, że działa tylko jak określę ścieżkę w konfiguracji a nie przez nazwę i kontrolkę Current VI's path...
A spróbuj podając nie samą ścieżkę tylko ścieżkę z nazwą pliku dll, tak jak w załączniku.
Zauważ, że current vi path daje nazwę ścieżki, w której jest vi plus na końcu nazwę vi, które tą funkcję wywołuje.

Co do obiektów ODev i Cdev to zerknę wieczorkiem, bo teraz nie bardzo mam na to czas.
A jak ręcznie podajesz podczas konfiguracji ścieżkę do dll i uruchomisz bez if(deviceX) to też się krzaczy?
Załączniki
snip.png
snip.png (15.79 KiB) Przejrzano 12433 razy
Dziech
Posty: 34
Rejestracja: 11 sie 2011 12:19
Wersja środowiska: LabVIEW 2010

Re: DLL C++

Post autor: Dziech »

Tak, też wylatuje błąd. Zaraz spróbuję porobić coś z tym co podałeś. Próbowałem debuggować z VS, ale daje błąd, nie pamiętam dokładnie, ale coś z uszkodzeniem stosu.


Zrobiłem jak napisałeś - wywala błąd

LabVIEW: An exception occurred within the external code called by a Call Library Function Node. The exception might have corrupted the LabVIEW memory. Save any work to a new location and restart LabVIEW.

Ale tylko w DLL node z case. Przy odczycie pliku nie czepia się...
ODPOWIEDZ