MS Visual Studio имеет графический интерфейс для разработки приложений и управления их проектами и решениями. Однако за кулисами всё выполняется консольными утилитами, так же входящими в состав этой IDE. В данной заметке я покажу маленький пример создания dll файла консольными средствами MS Visual Studio, с последующим его использованием в другом приложении. В качестве языка программирования использован C++. Весь приведённый ниже программный код можно набирать в любом текстовом редакторе (я использовал Notepad++).
На мой взгляд, владение консольными средствами построения проектов является необходимым, поскольку это позволяет избежать отношение к графическому интерфейсу IDE как к некоторому "магическому чёрному ящику".
Разработка библиотеки
Для начала, создадим каталог library, в котором будет размещаться проект нашей тестовой библиотеки. Затем в нём создадим подкаталоги:
- headers - заголовочные файлы
- obj - объектные файлы
- output - конечный результат компиляции
- sources - файлы исходного программного кода
В каталоге headers создаём файл some_library.h, в котором определяем интерфейс взаимодействия с нашей библиотекой. Подобных файлов может быть сколько угодно, в зависимости от функционала, разрабатываемой библиотеки. В данном примере ограничимся одним заголовочным файлом:
/*
some_library.h
© Andrey Bushman, 29/06/2013
Some library
*/
//--------------------------------------------------------------------
#ifndef SOME_LIBRARY_H
#define SOME_LIBRARY_H
#include <iostream>
#include <string>
#include <vector>
//
http://msdn.microsoft.com/ru-ru/library/a90k134d.aspx
#define DllExport __declspec( dllexport )
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace Some_namespace{
//--------------------------------------------------------------------
class DllExport Info
// Named info: phone, email,
site, e.t.c.
{
public:
std::string get_name() const;
void set_name(const std::string& val);
std::string get_number() const;
void set_number(const std::string& val);
Info(const std::string& number, const std::string& name);
~Info();
private:
std::string num;
std::string str;
};
//--------------------------------------------------------------------
struct DllExport Employee
// Employee info
{
public:
enum Sex{ male, female };
Employee(const std::string& name, const std::string& surname,
int age, Sex x);
static int get_emp_count();
std::string get_name() const;
void set_name(const std::string& val);
std::string get_surname() const;
void set_surname(const std::string& val);
int get_age() const;
void set_age(int val);
Sex get_sex() const;
void set_sex(Sex val);
std::vector<Info> phones;
std::vector<Info> emails;
std::vector<Info> sites;
~Employee();
private:
std::string nm; // name
std::string snm; // surname
int ag; // age
Sex sx;
static int emp_count;
};
//--------------------------------------------------------------------
DllExport void print(std::ostream& os, const Employee& emp);
//--------------------------------------------------------------------
}
#endif
Для того, чтобы пометить функции, классы и переменные используется, как правило __declspec (см. ссылку в комментариях кода).
Теперь в каталоге sources создадим файл some_library.cpp:
/*
some_library.cpp
© Andrey Bushman, 29/06/2013
Some library
*/
//--------------------------------------------------------------------
#include "../headers/some_library.h"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace Some_namespace{
//--------------------------------------------------------------------
void print(std::ostream& os, const Employee& emp){
os << emp.get_name() << ' ' << emp.get_surname() << '
'
<< emp.get_age() << " age, "
<< ( emp.get_sex()
==
Employee::male ? "male" : "female" );
}
//--------------------------------------------------------------------
std::string Info::get_name() const { return str; }
//--------------------------------------------------------------------
void Info::set_name(const std::string& val) { str = val; }
//--------------------------------------------------------------------
std::string Info::get_number() const { return num; }
//--------------------------------------------------------------------
void Info::set_number(const std::string& val) { num = val; }
//--------------------------------------------------------------------
Info::Info(const std::string& number, const std::string& name)
: num(number), str(name) {
std::cout << "Info::Info(): "
<< this
<< std::endl;
}
//--------------------------------------------------------------------
Info::~Info() {
std::cout << "Info::~Info(): "
<< this
<< std::endl;
}
//--------------------------------------------------------------------
Employee::Employee(const std::string& name, const std::string&
surname, int age, Sex x): nm(name), snm(surname), ag(age),
sx(x){
++Employee::emp_count;
std::cout << "Employee::Employee(): " << this
<< std::endl;
}
//--------------------------------------------------------------------
int Employee::emp_count = 0;
//--------------------------------------------------------------------
int Employee::get_emp_count(){
return Employee::emp_count;
}
//--------------------------------------------------------------------
std::string Employee::get_name() const { return nm; }
//--------------------------------------------------------------------
void Employee::set_name(const std::string& val) { nm = val; }
//--------------------------------------------------------------------
std::string Employee::get_surname() const { return snm; }
//--------------------------------------------------------------------
void Employee::set_surname(const std::string& val) { snm = val; }
//--------------------------------------------------------------------
int Employee::get_age() const { return ag; }
//--------------------------------------------------------------------
void Employee::set_age(int val) { ag = val; }
//--------------------------------------------------------------------
Employee::Sex Employee::get_sex() const { return sx; }
//--------------------------------------------------------------------
void Employee::set_sex(Sex val) { sx = val; }
//--------------------------------------------------------------------
Employee::~Employee(){
--Employee::emp_count;
std::cout << "Employee::~Employee(): " << this
<< std::endl;
}
//--------------------------------------------------------------------
}
Для того, чтобы для каждой компиляции не запускать один и тот же набор команд вручную, в каталоге library создадим простой make файл, назвав его makefile.mak:
# makefile.mak
# © Andrey Bushman, 29/06/2013
# ЗАДАЧА: Создание тестовой библиотеки some_library.dll
# СБОРКА ПРОЕКТА ОСУЩЕСТВЛЯЕТСЯ КОМАНДОЙ: nmake -f makefile.mak
#---------------------------------------------------------------------
# создание dll файла:
./output/some_library.dll: ./obj/some_library.obj
link /DLL /OUT:./output/some_library.dll ./obj/some_library.obj
#---------------------------------------------------------------------
# создание lib и exp файлов:
./output/some_library.lib: ./obj/some_library.obj
lib /DEF ./obj/some_library.obj
#---------------------------------------------------------------------
# создание obj файла:
./obj/some_library.obj: ./headers/some_library.h ./sources/some_library.cpp
cl /c /EHsc /Fo./obj/ ./sources/some_library.cpp
#---------------------------------------------------------------------
Теперь из командной строки Visual Studio переходим в каталог library и запускаем команду:
nmake -f makefile.mak
В результате, в каталоге obj появится файл some_library.obj, а в каталоге output - файлы some_library.exp, some_library.lib и some_library.dll. Наша библиотека готова к использованию.
Имея на руках lib файл, всегда можно посмотреть перечень сигнатур всех функций, которые экспортируются этой библиотекой. Для этого из командной строки Visual Studio переходим в каталог интересующего нас lib файла и запускаем команду:
dumpbin /EXPORTS some_library.lib /OUT:output.txt
В результате будет создан файл output.txt в котором, помимо прочего, будет присутствовать и перечень сигнатур экспортируемых функций (обратите внимание на подсвеченный текст):
Имея на руках lib файл, всегда можно посмотреть перечень сигнатур всех функций, которые экспортируются этой библиотекой. Для этого из командной строки Visual Studio переходим в каталог интересующего нас lib файла и запускаем команду:
dumpbin /EXPORTS some_library.lib /OUT:output.txt
В результате будет создан файл output.txt в котором, помимо прочего, будет присутствовать и перечень сигнатур экспортируемых функций (обратите внимание на подсвеченный текст):
??0Employee@Some_namespace@@QEAA@AEBU01@@Z (public: __cdecl Some_namespace::Employee::Employee(struct Some_namespace::Employee const &))
??0Employee@Some_namespace@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0HW4Sex@01@@Z (public: __cdecl Some_namespace::Employee::Employee(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int,enum Some_namespace::Employee::Sex))
??0Info@Some_namespace@@QEAA@AEBV01@@Z (public: __cdecl Some_namespace::Info::Info(class Some_namespace::Info const &))
??0Info@Some_namespace@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z (public: __cdecl Some_namespace::Info::Info(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
??1Employee@Some_namespace@@QEAA@XZ (public: __cdecl Some_namespace::Employee::~Employee(void))
??1Info@Some_namespace@@QEAA@XZ (public: __cdecl Some_namespace::Info::~Info(void))
??4Employee@Some_namespace@@QEAAAEAU01@AEBU01@@Z (public: struct Some_namespace::Employee & __cdecl Some_namespace::Employee::operator=(struct Some_namespace::Employee const &))
??4Info@Some_namespace@@QEAAAEAV01@AEBV01@@Z (public: class Some_namespace::Info & __cdecl Some_namespace::Info::operator=(class Some_namespace::Info const &))
?emp_count@Employee@Some_namespace@@0HA (private: static int Some_namespace::Employee::emp_count)
?get_age@Employee@Some_namespace@@QEBAHXZ (public: int __cdecl Some_namespace::Employee::get_age(void)const )
?get_emp_count@Employee@Some_namespace@@SAHXZ (public: static int __cdecl Some_namespace::Employee::get_emp_count(void))
?get_name@Employee@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Employee::get_name(void)const )
?get_name@Info@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Info::get_name(void)const )
?get_number@Info@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Info::get_number(void)const )
?get_sex@Employee@Some_namespace@@QEBA?AW4Sex@12@XZ (public: enum Some_namespace::Employee::Sex __cdecl Some_namespace::Employee::get_sex(void)const )
?get_surname@Employee@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Employee::get_surname(void)const )
?print@Some_namespace@@YAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEBUEmployee@1@@Z (void __cdecl Some_namespace::print(class std::basic_ostream<char,struct std::char_traits<char> > &,struct Some_namespace::Employee const &))
?set_age@Employee@Some_namespace@@QEAAXH@Z (public: void __cdecl Some_namespace::Employee::set_age(int))
?set_name@Employee@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Employee::set_name(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_name@Info@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Info::set_name(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_number@Info@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Info::set_number(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_sex@Employee@Some_namespace@@QEAAXW4Sex@12@@Z (public: void __cdecl Some_namespace::Employee::set_sex(enum Some_namespace::Employee::Sex))
?set_surname@Employee@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Employee::set_surname(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
??0Employee@Some_namespace@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0HW4Sex@01@@Z (public: __cdecl Some_namespace::Employee::Employee(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,int,enum Some_namespace::Employee::Sex))
??0Info@Some_namespace@@QEAA@AEBV01@@Z (public: __cdecl Some_namespace::Info::Info(class Some_namespace::Info const &))
??0Info@Some_namespace@@QEAA@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@0@Z (public: __cdecl Some_namespace::Info::Info(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
??1Employee@Some_namespace@@QEAA@XZ (public: __cdecl Some_namespace::Employee::~Employee(void))
??1Info@Some_namespace@@QEAA@XZ (public: __cdecl Some_namespace::Info::~Info(void))
??4Employee@Some_namespace@@QEAAAEAU01@AEBU01@@Z (public: struct Some_namespace::Employee & __cdecl Some_namespace::Employee::operator=(struct Some_namespace::Employee const &))
??4Info@Some_namespace@@QEAAAEAV01@AEBV01@@Z (public: class Some_namespace::Info & __cdecl Some_namespace::Info::operator=(class Some_namespace::Info const &))
?emp_count@Employee@Some_namespace@@0HA (private: static int Some_namespace::Employee::emp_count)
?get_age@Employee@Some_namespace@@QEBAHXZ (public: int __cdecl Some_namespace::Employee::get_age(void)const )
?get_emp_count@Employee@Some_namespace@@SAHXZ (public: static int __cdecl Some_namespace::Employee::get_emp_count(void))
?get_name@Employee@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Employee::get_name(void)const )
?get_name@Info@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Info::get_name(void)const )
?get_number@Info@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Info::get_number(void)const )
?get_sex@Employee@Some_namespace@@QEBA?AW4Sex@12@XZ (public: enum Some_namespace::Employee::Sex __cdecl Some_namespace::Employee::get_sex(void)const )
?get_surname@Employee@Some_namespace@@QEBA?AV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@XZ (public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > __cdecl Some_namespace::Employee::get_surname(void)const )
?print@Some_namespace@@YAXAEAV?$basic_ostream@DU?$char_traits@D@std@@@std@@AEBUEmployee@1@@Z (void __cdecl Some_namespace::print(class std::basic_ostream<char,struct std::char_traits<char> > &,struct Some_namespace::Employee const &))
?set_age@Employee@Some_namespace@@QEAAXH@Z (public: void __cdecl Some_namespace::Employee::set_age(int))
?set_name@Employee@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Employee::set_name(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_name@Info@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Info::set_name(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_number@Info@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Info::set_number(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
?set_sex@Employee@Some_namespace@@QEAAXW4Sex@12@@Z (public: void __cdecl Some_namespace::Employee::set_sex(enum Some_namespace::Employee::Sex))
?set_surname@Employee@Some_namespace@@QEAAXAEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@Z (public: void __cdecl Some_namespace::Employee::set_surname(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &))
Внимание: __cdecl - это одно из возможных соглашений о вызовах. О нём и о других соглашениях можно почитать здесь.
Разработка приложения
Теперь создадим каталог application, в котором будет размещаться проект приложения, использующего только что созданную нами библиотеку. Затем в каталоге application создадим подкаталоги:
- headers - заголовочные файлы
- obj - объектные файлы
- libs - lib файлы внешних библиотек
- output - конечный результат компиляции
- sources - файлы исходного программного кода
В каталоге headers создаём файл import_some_library.h:
/*
import_some_library.h
© Andrey Bushman, 29/06/2013
*/
//--------------------------------------------------------------------
#ifndef IMPORT_SOME_LIBRARY_H
#define IMPORT_SOME_LIBRARY_H
#include <iostream>
#include <string>
//
http://msdn.microsoft.com/ru-ru/library/a90k134d.aspx
#define DllImport __declspec( dllimport )
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
namespace Some_namespace{
//--------------------------------------------------------------------
class DllImport Info
// Named info: phone, email,
site, e.t.c.
{
public:
std::string get_name() const;
void set_name(const std::string& val);
std::string get_number() const;
void set_number(const std::string& val);
Info(const std::string& number, const std::string& name);
~Info();
private:
std::string num;
std::string str;
};
//--------------------------------------------------------------------
struct DllImport Employee
// Employee info
{
public:
enum Sex{ male, female };
Employee(const std::string& name, const std::string& surname,
int age, Sex x);
static int get_emp_count();
std::string get_name() const;
void set_name(const std::string& val);
std::string get_surname() const;
void set_surname(const std::string& val);
int get_age() const;
void set_age(int val);
Sex get_sex() const;
void set_sex(Sex val);
std::vector<Info>
phones;
std::vector<Info>
emails;
std::vector<Info>
sites;
~Employee();
private:
std::string nm; // name
std::string snm; // surname
int ag; // age
Sex sx;
static int emp_count;
};
//--------------------------------------------------------------------
DllImport void print(std::ostream& os, const Employee& emp);
//--------------------------------------------------------------------
}
#endif
Обратите внимание на то, что файл import_some_library.h отличается от заголовочного файла some_library.h: вместо строки кода
#define DllExport __declspec( dllexport )
в этот раз используется
#define DllImport __declspec( dllimport )
и, кроме того, везде вместо DllExport применялся DllImport.
Теперь создаём заголовочный файл нашего приложения, назвав его bush_main.h:
/*
bush_main.h
© Andrey Bushman, 28/06/2013
ЗАДАЧА:
использовать функционал библиотеки some_library.lib
*/
//--------------------------------------------------------------------
#ifndef BUSH_MAIN_H
#define BUSH_MAIN_H
#include <iostream>
#include <exception>
#include <string>
#include <vector>
#include "../headers/import_some_library.h"
using namespace std;
#endif
В подкаталог libs копируем файлы some_library.exp и some_library.lib, скомпилированный нами в первом проекте (см. подкаталог library/output). Затем в каталоге sources создаём файл main.cpp:
/*
main.cpp
©
Andrey Bushman, 28/06/2013
ЗАДАЧА:
использовать функционал библиотеки some_library.dll
*/
//--------------------------------------------------------------------
#include "../headers/bush_main.h"
//====================================================================
int main()
try{
namespace A = Some_namespace;
cout
<< "(c)
Andrey Bushman, 28/06/2013" << endl;
A::Employee emp("Jon", "Smit", 30, A::Employee::male);
cout
<< endl << "~~~~" << endl;
emp.phones.push_back(A::Info("123-456-789", "work"));
A::print(cout,
emp);
cout
<< endl << "~~~~" << endl;
A::Employee emp2("Mery", "White", 45, A::Employee::female);
A::print(cout,
emp2);
cout
<< endl << "~~~~" << endl;
int count = A::Employee::get_emp_count();
cout
<< "count:
"
<< count << endl;
}
catch(exception& e){
cerr
<< e.what() << endl;
return 1;
}
catch(...){
cerr
<< "Unknown
exception." << endl;
return 2;
}
Ну и, в заключении, в каталоге application создадим make файл makefile.mak, подобно тому, как мы это сделали выше для нашей библиотеки:
# makefile.mak
# © Andrey Bushman, 28/06/2013
# ЗАДАЧА: Создание тестового приложения app.exe
# СБОРКА ПРОЕКТА ОСУЩЕСТВЛЯЕТСЯ КОМАНДОЙ: nmake -f makefile.mak
#---------------------------------------------------------------------
# создание exe файла:
./output/main.exe: ./obj/main.obj
cl /Fe./output/app.exe /EHsc ./obj/main.obj ./libs/some_library.lib
#---------------------------------------------------------------------
# создание obj файла:
./obj/main.obj: ./headers/bush_main.h ./headers/import_some_library.h ./sources/main.cpp ./libs/some_library.lib ./libs/some_library.exp
cl /c /EHsc /Fo./obj/ ./sources/main.cpp
#---------------------------------------------------------------------
Теперь из командной строки Visual Studio переходим в каталог application и запускаем команду:
nmake -f makefile.mak
В результате, в каталоге obj появится файл main.obj, а в каталоге output - файл app.exe. Однако, для работы нашего файла app.exe необходим файл some_library.dll, скомпилированный нами в первом проекте. Поэтому копируем файл some_library.dll в тот же каталог, где находится app.exe.
Всё. Теперь, запустив программу app.exe мы увидим в консоли следующий вывод:
(c) Andrey Bushman, 28/06/2013
Employee::Employee(): 000000000027FBB0
~~~~
Info::Info(): 000000000027FB70
Info::~Info(): 000000000027FB70
Jon Smit 30 age, male
~~~~
Employee::Employee(): 000000000027FC40
Mery White 45 age, female
~~~~
count: 2
Employee::~Employee(): 000000000027FC40
Employee::~Employee(): 000000000027FBB0
Info::~Info(): 000000000036CCC0
Employee::Employee(): 000000000027FBB0
~~~~
Info::Info(): 000000000027FB70
Info::~Info(): 000000000027FB70
Jon Smit 30 age, male
~~~~
Employee::Employee(): 000000000027FC40
Mery White 45 age, female
~~~~
count: 2
Employee::~Employee(): 000000000027FC40
Employee::~Employee(): 000000000027FBB0
Info::~Info(): 000000000036CCC0
В дальнейшем, если нам потребуется обновить dll библиотеку, используемую нашим приложением, достаточно будет лишь заменить dll файл его более новой версией, без необходимости перекомпиляции самого приложения.
Архив с исходным кодом можно скачать отсюда: dll_using.zip (7 KB)
Дополнительные полезные ссылки, имеющие отношение к данной теме:
3 комментария:
+ к сказанному - для компиляции dll (да и exe) с "основных" .Net языков, VisualStudio вообще не нужен. Полноценные компиляторы есть в составе "генеральных" версий .Net (2.0, 4.0) - в папке %windir%\microsoft.net\framework в поддирикториях соотв. версий лежат компиляторы: ilasm.exe - IL ассемблер, csc.exe- С#, vbc.exe Visual Basic, jsc.exe - JScript.
+ к сказанному - для компиляции dll (да и exe) с "основных" .Net языков, VisualStudio вообще не нужен. Полноценные компиляторы есть в составе "генеральных" версий .Net (2.0, 4.0) - в папке %windir%\microsoft.net\framework в поддирикториях соотв. версий лежат компиляторы: ilasm.exe - IL ассемблер, csc.exe- С#, vbc.exe Visual Basic, jsc.exe - JScript.
да, я в курсе. :)
Отправить комментарий