Tworzenie DLL

0

Witam,
Potrzebuję napisać bibliotekę. Jako, że nigdy tego nie robiłem, chciałem sprawdzić z czym to się je i napisać coś bardzo małego na szybko.
No i mam w VS15 dwa projekty:
1 math, który jest właśnie biblioteką dll, 2 pliki:
mathfuncs.h:

#pragma once

namespace math
{
	class mathfuncs
	{
	public:
		double plus(double, double);
		double minus(double, double);
		
	};

}

mathfuncs.cpp:

#include "mathfuncs.h"

double math::mathfuncs::plus(double a, double b)
{
	return a + b;
}

double math::mathfuncs::minus(double a, double b)
{
	return a - b;
}

We właściwościach mam Configuratin Type: Dynamic Library (.dll).
Buduję, dostaję* math.dl*. Jest ok, jak na razie :)

2 mathapp, który będzie wykorzystywał math.dll.
mathapp.cpp:

// mathapp.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <iostream>
#include "mathfuncs.h"

using namespace std;
using namespace math;


int main()
{
	double a = 2.1, b = 3.14;
	mathfuncs math;
	cout << math.plus(a, b) << '\n';
	cout << math.minus(a, b) << '\n';

	cout << "\nKONIEC";
	std::cin.clear();
	std::cin.get();
        return 0;
}

*math.dll *oraz mathfuncs.h i mathfuncs.cpp skopiowałem do folderu z projektem mathapp. We właściwościach Configuration Properties->VC++ Directories:
Include Directories oraz Library Directories dałem ścieżkę do folderu z projektem mathapp

Przy próbie zbudowania dostaję dwa błędy:

1>mathapp.obj : error LNK2019: unresolved external symbol "public: double __thiscall math::mathfuncs::plus(double,double)" (?plus@mathfuncs@math@@QAENNN@Z) referenced in function _main
1>mathapp.obj : error LNK2019: unresolved external symbol "public: double __thiscall math::mathfuncs::minus(double,double)" (?minus@mathfuncs@math@@QAENNN@Z) referenced in function _main

Z tego co wiem, to chodzi o to, że linker nie widzi mathfuncs.cpp. Jak mam to zrobić, żeby nie było potrzeby ręcznego #include "mathfuncs.cpp" lub dodawania go do Resources files?

Pozdrawiam,
Fryderyk.

2

Gdy ostatni raz to robiłem na windowsie (ładnych kilka lat temu) musiałem użyć dodatkowych "dekoratorów" pod tytułem dllimport, dllexport .

2

Z tego co wiem, to chodzi o to, że linker nie widzi mathfuncs.cpp.

Czekaj, czekaj. Ale dlaczego ma widzieć, jeśli chcesz załączyć bibliotekę DLL? W projekcie mathapp powinieneś dodać bibliotekę importów math.lib (jeśli jest).

W pliku mathfuncs.h powinieneś zrobić:

#ifdef DLL_BUILD
#	define DLLEXPORT __declspec( dllexport )
#else
#	define DLLEXPORT __declspec( dllimport )
#endif

class DLLEXPORT mathfuncs {  ...  }; 

W opcjach projektu dll-ki zdefiniuj DLL_BUILD.

0
several napisał(a):

Gdy ostatni raz to robiłem (ładnych kilka lat temu) musiałem użyć dodatkowych "dekoratorów" pod tytułem dllimport, dllexport .

Bez tego dostaj:
math.dll
math.ilk
math.pdb
Z __declspec( dllexport ) dostaję dodatkowo:
math.lib
math.exp

Aczkolwiek błędy dalej te same.

0
0x666 napisał(a):

Z tego co wiem, to chodzi o to, że linker nie widzi mathfuncs.cpp.

Czekaj, czekaj. Ale dlaczego ma widzieć, jeśli chcesz załączyć bibliotekę DLL? W projekcie mathapp powinieneś dodać bibliotekę importów math.lib (jeśli jest).

W pliku mathfuncs.h powinieneś zrobić:

#ifdef DLL_BUILD
#	define DLLEXPORT __declspec( dllexport )
#else
#	define DLLEXPORT __declspec( dllimport )
#endif

class DLLEXPORT mathfuncs {  ...  }; 

W opcjach projektu dll-ki zdefiniuj DLL_BUILD.

Dzięki, działa, ale mam jeszcze pytanie.

W math Property Pages->Configuration Properties->C/C++->Preprocessor->Preprocessor Definitions dodałem DLL_BUILD.
W mathfuncs.h zrobiłem jak piszesz.

W Projekcie mathapp stworzyłem folder DLL, a w nim foldery dll, lib, include. do dll wrzuciłem math.dll, do lib math.lib a do include mathfuncs.h i mathfuncs.cpp.
Chciałbym to zrozumieć, dlatego rozpisuję tak szczegółowo.
W mathapp Property Pages->Configuration Properties->VC++ Directories->Include Directories dodałem ścieżkę do folderu include.
W mathapp Property Pages->Configuration Properties->VC++ Directories->Lirbary Directories dodałem ścieżkę do folderu dll.
W mathapp Property Pages->Configuration Properties->Linker->General->Additional Library Directories dodałem ścieżkę do folderu lib.
W mathapp Property Pages->Configuration Properties->Linker->Imput->Additional Dependencies dodałem math.lib.
Czy tak to ma wyglądać?

Program wyrzucił błąd, że nie widzi math.dll. Dopiero jak dodałem go do głównego folderu z projektem to się skompilował.
Czy jest jakaś opcja, żeby .dll nie był w tym samym folderze co .exe a w podfolderze?

I jeszcze:

#ifdef DLL_BUILD
#define DLLEXPORT __declspec( dllexport )
#else
#define DLLEXPORT __declspec( dllimport )
#endif

class DLLEXPORT mathfuncs {  ...  }; 

A jeśli mam kilka klas to w każdym pliku .h mam to dodawać? Nie lepiej by było dać:

#ifndef DLLEXPORT
#define DLLEXPORT __declspec( dllexport )
#endif

Dlaczego jesli DLL_BUILD nie jest zdefiniowany do DLLEXPORT definiujemy jako dllimoprt?

1

I jeszcze:

#ifdef DLL_BUILD
#define DLLEXPORT __declspec( dllexport )
#else
#define DLLEXPORT __declspec( dllimport )
#endif

class DLLEXPORT mathfuncs {  ...  }; 

To jest prawidłowy kod.

#ifndef DLLEXPORT
#define DLLEXPORT __declspec( dllexport )
#endif

Nie, bo chodzi o to by makro DLLEXPORT oznaczało __declspec(dllexport) podczas kompilowania DLL-ki, ale nie podczas kompilowania programu exe.
Proponuję też zamiast DLLEXPORT nazwać makro sensowniej, np. MATHAPI gdzie math to nazwa dll-ki.

Dlaczego jesli DLL_BUILD nie jest zdefiniowany do DLLEXPORT definiujemy jako dllimoprt?

Kiedy kompilujesz DLL, jest zdefiniowane DLL_BUILD i masz __declspec(dllexport) przy każdej eksportowanej funkcji.
Kiedy kompilujesz EXE, DLL_BUILD nie jest zdefiniowane, i mimo że używasz tego samego nagłówka, te same funkcje są teraz dllimport a nie dllexport.
dllexport jest wymagane do wyeksportowania funkcji z dll-ki.
dllimport podczas importowania funkcji nie jest ściśle konieczne - może być pusto w tym miejscu. Program się skompiluje i będzie działać bez tego, ale dllimport generuje wydajniejszy kod.

1

Program wyrzucił błąd, że nie widzi math.dll

DLL-ka powinna być tam gdzie exec. Na czas debugowania możesz ustawić ścieżkę do biblioteki w opcjach projektu: w Debugging -> Environment daj PATH=%PATH%;<ścieżka do folderu z dll-ką>. To rozwiązanie jest wygodniejsze, zważywszy, że plik biblioteki leży w katalogach innego projektu.

W mathapp Property Pages->Configuration Properties->VC++ Directories->Lirbary Directories dodałem ścieżkę do folderu dll.

Nie, tu dajesz ścieżkę do katalogu z plikiem .lib. W przypadku DLL-ki wystarczy ustawić dwie ścieżki - library i include directories.

0
fryderykst napisał(a):

Ok, got it. A co jeśli mam kilka klas? W każdym pliku .h to definiuję? W zasadzie wydaje mi się to najbardziej logiczne, ale wolę zapytać. -

DLLka powinna mieć jeden, główny plik .h.

0
Azarien napisał(a):
fryderykst napisał(a):

Ok, got it. A co jeśli mam kilka klas? W każdym pliku .h to definiuję? W zasadzie wydaje mi się to najbardziej logiczne, ale wolę zapytać. -

DLLka powinna mieć jeden, główny plik .h.

No tak, a w nim dołączone wszystkie inne. Ale jak w głównym pliku zdefiniuje makro to będzie ono widoczne w plikach które do niego dołączę?

0

Nie, chyba że dołączysz je za definicją makra.

0
0x666 napisał(a):

Nie, chyba że dołączysz je za definicją makra.

No coś nie działa.

dodałem do projektu plik math.h:

#pragma once
#ifdef DLL_BUILD
#   define DLLEXPORT __declspec( dllexport )
#else
#   define DLLEXPORT __declspec( dllimport )
#endif

#include "mathfuncs.h"

z mathfuncs.h usunąłem:

#ifdef DLL_BUILD
#   define DLLEXPORT __declspec( dllexport )
#else
#   define DLLEXPORT __declspec( dllimport )
#endif

I biblioteka się nie kompiluje, tak jak myślałem.

Chodzi mnie o to, czy w każdym pliku .h z nową klasą muszę wrzucać to:

#ifdef DLL_BUILD
#   define DLLEXPORT __declspec( dllexport )
#else
#   define DLLEXPORT __declspec( dllimport )
#endif

Czy można to zrobić w jednym miejscu?

0x666 napisał(a):

Nie, tu dajesz ścieżkę do katalogu z plikiem .lib. W przypadku DLL-ki wystarczy ustawić dwie ścieżki - library i include directories.

Z tego co widzę, to trzeba ustawić 3 rzeczy:
Property Pages->Configuration Properties->VC++ Directories->Include Directories ścieżka do folderu, w którym są pliki** H i CPP**
Property Pages->Configuration Properties->VC++ Directories->Lirbary Directories ścieżka do folderu, w którym jest plik .lib
Property Pages->Configuration Properties->Linker->Imput->Additional Dependencies dodać plik .lib

0

No coś nie działa.

A który plik załączasz do plików źródłowych? math.h czy mathfuncs.h?

Zrób inaczej. Stwórz sobie plik np. dllexport.h, daj w nim:

#pragma once
#ifdef DLL_BUILD
#   define DLLEXPORT __declspec( dllexport )
#else
#   define DLLEXPORT __declspec( dllimport )
#endif

I dołączaj go do wszystkich nagłówków, które używają makra DLLEXPORT. Takie rzeczy robi się raczej przy bardziej rozbudowanych bibliotekach. Przy tych mniejszych wszystko, co jest eksportowane, mieści się w jednym pliku nagłówkowym.

Z tego co widzę, to trzeba ustawić 3 rzeczy:

Jest tak, jak pisałem. Opcjonalnie możesz dodać ścieżkę do plików .cpp, jeśli chcesz mieć możliwość wygodnego debugowania swojej biblioteki, skompilowanej w wersji debug. O dodaniu samej biblioteki .lib nie wspominałem, bo to oczywiste ;)

1

I biblioteka się nie kompiluje, tak jak myślałem.

Bo coś źle zrobiłeś.

Musisz zrozumieć co robi #include.

#include wkleja „tu i teraz”, dosłownie, zawartość inkluzowanego pliku.
Jeśli robisz #include "math.h" to tak jakbyś w tym miejscu wstawił metodą kopiuj-wklej cały plik math.h.
Odstępstwem od tej reguły jest dyrektywa #pragma once, ale można sobie wyobrazić ją jako lukier składniowy na klasyczny wartownik z #ifndefem.

0

Dziękuję wszystkim za pomoc. W zasadzie mam już to czego potrzebuję, a to z czym mam wątpliwości(include i makra) postaram się przetestować.

Mam jeszcze jedno pytanie.
Biblioteka, którą piszę(nazwijmy ją X.dll) korzysta z innej biblioteki(Y.dll). I teraz program, który korzysta z X.dll nie widzi biblioteki Y.dll, dopóki nie dodam jej tak samo jak w przypadku X.dll, czyli include oraz library directories. Oczywiście w projekcie X.dll dodałem też bibliotekę Y.dll. Jak to zrobić, żeby w programie korzystającym z X.dll wrzucić tylko plik Y.dll i nie przejmować się już plikami .lib oraz .h czy .cpp? Wiem, że się da, bo Y.dll również korzysta z innej biblioteki(Z.dl), i pisząc program wykorzystujący Y.dll nie musiałem już martwić się o Z.dll, tylko wrzuciłem plik .dll do projektu.

Troszkę to zagmatwane, mam nadzieję, że rozpisałem to w miarę jasno :D

0

Pewnie w pliku X.h używasz rzeczy zdefiniowanych w Y.h i stąd ta konieczność dodawania dodatkowych ścieżek.

0
0x666 napisał(a):

Pewnie w pliku X.h używasz rzeczy zdefiniowanych w Y.h i stąd ta konieczność dodawania dodatkowych ścieżek.

W zasadzie wcale z niej jeszcze nie korzystam, dodałem tylko

#include <Y.h>

w pliku Xaa.h, a w X.h dałem

#include "Xaa.h"
0

Na jedno wychodzi w tym kontekście.

0

No tak, ale jak mam korzystać z Y.dll w X.dll, nie dodając go? :D W zasadzie tak jak mam teraz mi odpowiada. Piszę pierwszą swoją bibliotekę, więc od czegoś trzeba zacząć, ale tak jak pisałem wcześniej, Y.dll korzysta z Z.dll, a nie musiałem nic grzebać z Z.dll. Tylko dostałem błąd o braku pliku Z.dll, wrzuciłem go do folderu z projektem i już. A tutaj muszę dodawać jeszcze pliki .h .cpp i .lib.

0

Jak się chce pisać DLL-kę, to warto przemyśleć sprawę, czy w ogóle warto, a jeśli warto, to trzeba się zastanowić, co powinno być widoczne "na zewnątrz". Z drugiej strony nie ma nic złego w tym, że biblioteka wymaga obecności dwóch lub więcej plików nagłówkowych.

0

Czy warto, ciężko powiedzieć :) Mam program, który napisałem na szybko i bardzo chaotycznie, chciałem go poukładać i ubrać w szatę graficzną, a że DLL nigdy nie pisałem, to stwierdziłem, ze czegoś się przy okazji nauczę :) Więc układam to co program ma robić w klasy i funkcje w dll, żeby później wykorzystać to w program graficznym, który będę pisał chyba w QT.
Dzięki za wszelką pomoc i pozdrawiam :)

0

Z DLL-kami w C++ trzeba uważać, bo są praktycznie nieprzenośne między kompilatorami. Zatem jeśli bibliotekę robisz w Visualu, to zapomnij o użyciu jej w programach kompilowanych na MinGW.

0

Czyli mówisz, że to co chce zrobić się nie uda raczej?

0

Jeśli będziesz używał różnych kompilatorów, to nie. Dlatego większość bibliotek C++ prawie zawsze są rozprowadzana w postaci źródeł, do skompilowania przez programistę. A jeśli są dostępne binarki, to pod konkretny kompilator i wersję.

0

Jeśli napiszę to w visualu, a później wrzucę w QT i skompiluję(o ile się uda), to będzie ok?
W QT pracowałem jakiś czas temu i tylko chwilę, więc nie za bardzo pamiętam jak to dział, aczkolwiek pamiętam, że przyjemnie się pisało.

0

Jeśli aplikację Qt kompilujesz kompilatorem visuala, tym samym, którym kompilowałeś swoje DLL-ki, to wszystko powinno być ok.

0

Jeszcze raz dzięki za pomoc i do usłyszenia. Coś czuję, że jak zacznę działać z QT, to będzie miliard pytań. Ale jak nie spróbuję, nie będę wiedział co i jak.

Pozdrawiam!

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