Страницы

Поиск по вопросам

Показаны сообщения с ярлыком dll. Показать все сообщения
Показаны сообщения с ярлыком dll. Показать все сообщения

понедельник, 13 апреля 2020 г.

Вызов функции из DLL из командной строки

#cpp #dll

                    
у меня есть функция в DLL:

extern "C" __declspec(dllexport) void test(char *text, len);


Как правильно её вызвать из командной строки? Пока пытаюсь делать так:

rundll32 mydll.dll,test "string" 6

    


Ответы

Ответ 1



Почитайте описание как использовать rundll32. Вкратце - не любая ф-ция может быть вызвана из библиотеки, а строго следующая определенным соглашениям, описанным в статье по ссылке. Если по-простому и по-русски, то rundll32 поддерживает только ф-ции со следующей сигнатурой: void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow); Обратите внимание на аргументы и на то, что ф-ция должна следовать соглашению _stdcall, а не _cdecl.

вторник, 7 апреля 2020 г.

Как уменьшить зависимость заголовочных файлов C++?

#cpp #dll

                    
Допустим есть файл A1.h с описанием класса A1 (class A1 { ...};), файл A2.h с описанием
класса A2 (class A2 {...};). Есть файл B1.h, в котором подключаются заголовочные файлы
A1.h и A2.h и описывается класс B1 (

#include "A1.h"
#include "A2.h"

class B1 {
A1* pA1;
A2* pA2;
...
};


). Как уменьшить зависимость заголовочных файлов, чтобы когда подключалась dll библиотека
не нужно было тянуть за собой много заголовочных файлов ?
    


Ответы

Ответ 1



В вашем примере в файле B1.h нет необходимости подключать файлы A1.h и A2.h, так как описанный вами класс В1 содержит лишь указатели на классы А1 и А2. Достаточно будет предварительного объявления: class A1; class A2; class B1 { A1* pA1; A2* pA2; }; А заголовочные файлы А1.h и А2.h необходимо подключить в файле реализации B1.cpp.

воскресенье, 15 марта 2020 г.

Статическое внедрение dll в сборку

#c_sharp #net #dll #assembly


Нужно при старте Win-приложения загружать dll, но делать это необходимо изнутри сборки
(именно потому статическая загрузка)!

Частичное решение уже есть: https://stackoverflow.com/questions/23971418/c-sharp-embed-dll-in-exe-filenotfoundexception

Но, что и закономерно, в моем случае также вылетает это же исключение: 


  An unhandled exception of type 'System.IO.FileNotFoundException' occurred in mscorlib.dll
  
  Additional information: Не удалось загрузить файл или сборку "SevenZipSharp, Version=0.64.3890.29348,
Culture=neutral, PublicKeyToken=20de82c62b055c88" либо одну из их зависимостей. Не
удается найти указанный файл.


Пробовал добавлять эту dll и через ресурсы (тогда программа даже отказывается стартовать
из-за того, что не находит в нужном месте эту dll, а именно в папке "bin/Debug/...")
и через "Сборка - Add - Existing Item..." (так стартует, но до загрузки формы получаю
вышеописанное исключение).

Код, находящийся в файле Program.cs, имеет вид (практически идентичен тому, который
рассматривается по ссылке и оставлен без ответа; также я пробовал изменять его, следуя
указаниям из различных источников):

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);

            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }

        static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs
args)
        {
            string assemblyName = args.Name.Split(',').First();
            using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("WindowsFormsApplication1."
+ assemblyName + ".dll"))
            {
                byte[] assemblyData = new byte[stream.Length];
                stream.Read(assemblyData, 0, assemblyData.Length);
                return Assembly.Load(assemblyData);
            }
        }
    }
}


Также пробовал в свойствах dll (добавленной через "Existing Item...") выбирать различные
варианты "Build Action". Результат отрицательный.

Также вариант с программой ILMerge (http://habrahabr.ru/post/126089/) не подходит.

Как указать системе, что я хочу обратиться и загрузить dll по пути изнутри сборки,
а не извне?
    


Ответы

Ответ 1



Вот полные шаги: Добавить SevenZipSharp.dll в проект через Add Existing Item, выставить Build Type = Embedded Resource Добавить SevenZipSharp.dll в References. Выставить у референса Copy Local = false - чтобы избежать копирования в bin. Упомнятуть класс где-то в Form_Load (чтобы произошла попытка подгрузки dll) - у вас это явно сделано: private void Form1_Load(object sender, EventArgs e) { SevenZip.SevenZipCompressor c = new SevenZip.SevenZipCompressor(); } Аккуратно обработать CurrentDomain_AssemblyResolve: using System; using System.Reflection; using System.Windows.Forms; namespace WindowsFormsApplication7 { static class Program { [STAThread] static void Main() { AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve; Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.Run(new Form1()); } private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { var assemblyName = new AssemblyName(args.Name).Name; if (assemblyName == "SevenZipSharp") { using (var stream = typeof(Program).Assembly.GetManifestResourceStream( "WindowsFormsApplication7." + assemblyName + ".dll")) { byte[] assemblyData = new byte[stream.Length]; stream.Read(assemblyData, 0, assemblyData.Length); return Assembly.Load(assemblyData); } } else { return null; } } } } Это минимальный рабочий пример. Если не работает - запускайте под отладчиком. Скорее всего вы не угадали с именем ресурса, и GetManifestResourceStream возвращает null. Убедитесь, что тип у айтема выставлен именно в Embedded Resource (а не просто в Resource). Просмотреть имена всех доступных ресурсов можно прямо в отладчике, вызовом typeof(Program).Assembly.GetManifestResourceNames() Проект целиком на гитхабе: https://github.com/PashaPash/SevenZipSharp-Embedded

воскресенье, 23 февраля 2020 г.

Как в DLL использовать пользовательский тип?

#cpp #dll


Есть dll на C++ и проект на C++. Как мне сделать так, чтобы функция в dll принимала
ссылку на экземпляр некоторого класса (пользовательский тип, а не char или int) из
проекта и обрабатывала его? Нужно ли описание этого класса вынести в отдельный хедер
и включить его и в dll и в проект? Планируется использование динамического подключения
dll к проекту.
    


Ответы

Ответ 1



Нужно ли описание этого класса вынести в отдельный хедер и включить его и в dll и в проект? Да, именно так, если вы хотите полноценно использовать этот класс внутри вашей DLL. "Полноценно использовать" - это значит иметь возможность объявлять объекты этого класса и/или доступаться к его членам. Разумеется, если ваш класс содержит члены с не-inline определениями, то придется организовать соответствующую инфраструктуру. Члены класса будут объявляться в одном модуле (DLL или EXE), экспортироваться оттуда и импортироваться остальными модулями (DLL или EXE) (см. __declspec(dllexport) и пр.) Эти моменты зависят от реализации, но я подразумеваю, что вы ведете речь о MSVC.

воскресенье, 16 февраля 2020 г.

Как в DLL использовать пользовательский тип?

#cpp #dll


Есть dll на C++ и проект на C++. Как мне сделать так, чтобы функция в dll принимала
ссылку на экземпляр некоторого класса (пользовательский тип, а не char или int) из
проекта и обрабатывала его? Нужно ли описание этого класса вынести в отдельный хедер
и включить его и в dll и в проект? Планируется использование динамического подключения
dll к проекту.
    


Ответы

Ответ 1



Нужно ли описание этого класса вынести в отдельный хедер и включить его и в dll и в проект? Да, именно так, если вы хотите полноценно использовать этот класс внутри вашей DLL. "Полноценно использовать" - это значит иметь возможность объявлять объекты этого класса и/или доступаться к его членам. Разумеется, если ваш класс содержит члены с не-inline определениями, то придется организовать соответствующую инфраструктуру. Члены класса будут объявляться в одном модуле (DLL или EXE), экспортироваться оттуда и импортироваться остальными модулями (DLL или EXE) (см. __declspec(dllexport) и пр.) Эти моменты зависят от реализации, но я подразумеваю, что вы ведете речь о MSVC.

пятница, 14 февраля 2020 г.

Несовместимость dll, lib и a между компиляторами

#cpp #c #dll #lib #extern


Мне бы хотелось разобраться в вопросе (не)совместимости между различными компиляторами
C/C++. Часто оказывается, что между собой несовместимы даже разные версии одного и
того же компилятора. Доступной и детальной информации по этой теме мне найти не удалось.

Вопросы такие:

1) Я знаю, что с совместимостью компиляторов C++ все очень плохо, потому что язык
слишком переусложнен, имеет бесконечное количество тонкостей, исключений из правил
и пунктов, которые определяются реализацией. Поэтому я не очень понимаю, зачем используют
всякие export "C" и extern "C", если двоичные файлы (например, lib) практически всегда
оказываются совместимы лишь с тем компилятором, которым они сделаны. Так в чем смысл?

2) Если разработка lib, dll и a файлов ведется с использованием языка C, то как в
этом случае обстоят дела с совместимостью? Все так же плохо, как если бы разработка
велась на C++? Или нет?

3) Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная
однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий?
    


Ответы

Ответ 1



Несовместимость бинарных модулей (далее, для краткости, просто "модулей"), произведенных разными компиляторами, определяется в основном следующими тремя аспектами: Разные правила декорирования имен экспортируемых символов Разное устройство объектов стандартной библиотеки Разные правила расположения полей структур в памяти Первый пункт характерен фактически только для С++: в Си существует набор характерных для конкретной аппаратной платформы соглашений о вызове (например, stdcall, fastcall и cdecl для x86), которые довольно четко прописывают правила декорирования имен. Второй пункт относится и к Си и к С++, но в Си не очень много "объектов стандартной библиотеки" - в голову приходит только FILE*, и экспортировать его через границы модулей нет никакого смысла. Таким образом да, действительно можно сказать, что С++ "хуже" чем Си в плане бинарной совместимости. Это разумеется не значит, что не нужно на нем писать, это лишь значит, что на границе модулей нужно использовать интерфейс в стиле Си (либо использовать стандартизированный объектно-ориентированный интерфейс, например Component Object Model в Windows). Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий? Использование DLL на С/С++ в других языках это больше чем вопрос бинарного интерфейса (например, в них может просто не быть концепции заголовочных файлов, указателей и т.п.), но обычно да, библиотека с интерфейсом в стиле Си может быть использована и из других языков с тем или иным количеством дополнительных телодвижений. Рекомендации для обеспечения максимальной бинарной совместимости: Экспортируйте через границы бинарного модуля только простые функции с припиской extern "C" (т.е, никаких классов, шаблонов, перегруженных функций, пространств имен и т.п.) Передавайте через границы модулей только простые типы, указатели на них и указатели на функции. Если все же передаете структуры, сделайте первым членом структуры ее размер. Это позволит, если вы натолкнетесь на различия по выравниванию полей, обнаружить несоответствие в общем размере структуры и хотя бы нормально вернуть ошибку. Не передавайте через границы модулей объекты стандартной библиотеки, например указатели FILE*. Блоки динамической памяти должны освобождаться всегда в том же модуле, в котором были выделены. Т.е., если библиотека возвращает программе-клиенту указатель на блок памяти, выделенный malloc внутри себя, она должна предоставлять специальную функцию для его освобождения (вызывающую внутри себя free), вместо того, чтобы полагаться на вызов free в программе-клиенте.

Ответ 2



Толстый троллинг по поводу "недостатков" языка С++ продолжается. Ну что же, ринемся на защиту детища Страуструпа. 1) Я знаю, что с совместимостью компиляторов C++ все очень плохо, С совместимостью компиляторов С++ все очень хорошо, гораздо лучше, чем с совместимостью компиляторов с каких-либо других языков. Вплоть до того, что многие языки имеют компилятор только под одну платформу и/или от одного производителя. Конечно, такой подход улучшает совместимость, но ухудшает переносимость и тормозит развитие. При отказе единственного производителя компилятора от поддержки языка 100500 разработчиков повисают в воздухе. Достаточно вспомнить отказ даже такого гиганта как Микрософт от обратной совместимости в языке Visual Basic при переходе от версии VB6 к версии VB.net. потому что язык слишком переусложнен, имеет бесконечное количество тонкостей, исключений из правил и пунктов, которые определяются реализацией. Язык С++ не переусложнен. Просто в языке С++ не включена "защита от дурака". В награду за не включенную "защиту от дурака" пользователи имеют возможность делать то, что им нужно, а не то, что позволяет им язык. Чтобы пользоваться всеми возможностями языка С++ недостаточно выучить синтаксис С++ по первому изданию книжки Страуструпа. Надо еще читать книжки, которые выходят по мере развития языка и в которых обсуждаются как раз те новые возможности, которые некоторых разработчиков приводят к созданию крашащегося кода, а другим разработчикам позволяют съэкономить 100500 часов рабочего времени. Внесение в язык С++ новых возможностей (например шаблонов) порождает взаимодействие этих новых возможностей со старыми возможностями (например с классами). Это взаимодействие может служить как источником ошибок, так и источником новых идей и подходов. Многие достойные люди занимаются исследованиями в этих направлениях и публикуют свои результаты исследований. Изучая эти исследования можно избежать ошибок и воспользоваться новыми подходами. Еще раз повторю что в языке С++ не включена "защита от дурака". И поэтому, чтобы не делать ошибок в языке С++, надо точно понимать, что именно ты делаешь. Поэтому я не очень понимаю, зачем используют всякие export "C" и extern "C", Как тут правильно заметили это отключает манглинг имен. если двоичные файлы (например, lib) практически всегда оказываются совместимы лишь с тем компилятором, которым они сделаны. Так в чем смысл? Под совместимостью различных компиляторов языка С++ имеется ввиду совместимость на уровне исходного текста. Двоичную совместимость объектников, бинарников и библиотек lib никто и никогда не гарантировал. Кстати, это относится и к другим языкам (за исключением языков с виртуальными машинами, но там за это платят тем, что приходится за собой таскать все тупиковые решения и ошибки ради обратной совместимости). 2) Если разработка lib, dll и a файлов ведется с использованием языка C, то как в этом случае обстоят дела с совместимостью? Все так же плохо, как если бы разработка велась на C++? Или нет? Кстати, никто не гарантировал совместимость объектников, бинарников и библиотек lib для языка Си. Трансляторы с языка Си от разных производителей производят объектники разного формата. Ну и что в этом такого? 3) Есть ли способы делать двоичные библиотеки максимально совместимыми? Чтобы написанная однажды библиотека dll могла быть подключена в самых разных языках без боли и страданий? Если в системе Windows dll имеет "pure Си" интерфейс, то ее можно вызвать из многих других языков. Количество боли и страданий при этом зависит от разработчика интерфейса между dll и этим самым языком, из которого dll вызывается. Собственно языки С/С++ к этому не имеют никакого отношения. UPD1: Я не начинал толстый троллинг по поводу недостатков C++, просто это мое мнение, которое основывается на определенном опыте работы с этим языком, в том числе в международных группах разработчиков. – Максим 2 минуты назад Уважаемый Максим, количество использующих язык С++ разработчиков в мире (в том числе в международных группах разработчиков) убедительно показывает, что язык C++ вполне жизнеспособен. Если бы это было не так, то от языка С++ давно бы отказались, как отказались в свое время от 100500 других языков. UPD2: С тем, что С++ жизнеспособен, никто не спорит. Но все-таки в нем действительно много разных тонкостей и хитростей. – HolyBlackCat 10 секунд назад Как я уже сказал, все эти "тонкости и хитрости" это следствие того, что новые возможности добавляются в язык и на уровне транслятора отсутствуют семантические и синтаксические ограничения на использование этих новых возможностей. Также на уровне транслятора отсутствуют семантические и синтаксические ограничения на взаимодействие новых возможностей со старыми возможностями. Это, с одной стороны, добавляет мощи. А с другой стороны, ограничения на использование новых средств должны храниться в голове программиста, а не прошиваются в синтаксисе языка. В любом случае, все, кому С++ кажется слишком сложным/непонятным/переусложненным/плохо совместимым может пользоваться любыми другими языками вместо того, чтобы перечислять мнимые недостатки С++. Потому что все эти якобы недостатки С++ это вовсе не недостатки, а это такой подход при котором транслятор не мешает Вам выстрелить себе в ногу, но зато дает возможность выстрелить на 100500 километров при правильном использовании. На свете есть 100500 трансляторов, которые мешают программисту выстрелить себе в ногу, но зато и ограничивают возможности. UPD3: Извините за частое употребление идиомы 100500, но уж очень она тут подходит. :-)

четверг, 13 февраля 2020 г.

Как хранятся глобальные const данные в библиотеках C++

#cpp #dll #lib #namespace #const


Есть статическая библиотека (.lib/.a).
В этой библиотеке находится файл с namespace, в котором две const переменные с публичным
и приватным ключом:

namespace dsa
{
   const std::vector private_key = {...}
   const std::vector public_key = {...}
}


Публичный ключ используется в динамической библиотеке (.dll/.so), путем подключения
исходной статической библиотеки.

Приватный ключ используется в исполняемом файле, путем подключения исходной статической
библиотеки.

Поскольку статическая библиотека является слепком .obj файлов единиц трансляции,
то оба ключа будут в ней, это понятно.

А вот будет ли в публичную динамическую библиотеку (.dll/.so) попадать информация
о приватном ключе, если в ней не вызываются функции, использующие его?

Как вообще организовано хранение подобных данных (глобальные const НЕ POD данные)
в windows/linux файлах динамических библиотек?
    


Ответы

Ответ 1



Да в *.dll попадут оба ключа. В этом отношении, линковка dll ни чем не отличается от exe. Проверить наличие переменной можно попросив линкер генерировать map файл. Линкер может выкинуть (при подключении .lib файла), только единицу трансляции целиком (соответствующею одному .obj/.cpp, из тех, что попали в .lib), и только в случае, если нет ссылок ни на один символ из этой единицы трансляции. Но ссылка на публичный ключ используется в любом случае... Самый надежный способ - написать две статические либы: с приватным и с публичным ключами по отдельности, тем более, что они должны обеспечивать разную функциональность: передача и прием сообщений.

Ответ 2



Исходя из ответа @Chorkov провел несколько тестов с использованием /MAP и результаты огорчили. Решил хранить приватный ключ в отдельном файле, исключив его таким образом из кода вообще.

Как узнать к какому Reference относится та или иная библиотека

#c_sharp #winforms #dll


В папке bin/Release по итогу оказывается множет dll+xml в связке. Каким образом можно
узнать к какому Reference относится каждая dll?

По идее сколько References столько и dll должно быть в каталоге bin/Release, так?
У меня же это не так: дополнительно создаются другие dll, которые как-то имеют отношение
к References. Но как выявить это отношение?

Спасибо!

P.S. Я использую сторонние компоненты (DevExpress), но вопрос всё же общий.
    


Ответы

Ответ 1



В свойствах каждой Reference есть параметр CopyLocal. Если true - то соотв. дллка будет скопирована при компиляции в папку с бинарниками. Также туда попадут файлы которые добавлены в проект и в свойствах которых соответствующим образом установлено значение поля 'Copy to Output Directory', например это могут быть сторонние дллки, которые необходимы твоему приложению или другие файлы, например локальная база данных. Также в свойствах проекта в разделе 'Build Events' есть поля для записи скриптов выполняемых до и после компиляции. И там вполне могут быть инструкции копирования файлов (любых, куда угодно). И еще различные производители для локализации используют ресурсные дллки (по каждому языку), например производители контролов - точно. Эти дллки (или даже папки с дллками) в итоге тоже попадают в папку с бинарниками

Ответ 2



Открываете узел References, выбираете сборку, в контекстном меню жмете на Properties. В открывшейся вкладке Properties смотрите на имя файла в свойстве Path.

Ответ 3



Если проект создан в Visual Studio, то у проекта есть файл названиепроекта.csproj Это обычный xml. Откройте его в любом текстовом редакторе. В нем есть теги Reference и бывают COMReference. В некоторых из тегов Reference указаны имена dll файлов, часть из которых соответствует файлам в \bin\Release. .NET-сборку (dll или exe) можно открыть в ildasm.exe и посмотреть зависимости.

понедельник, 10 февраля 2020 г.

Как запустить функцию в потоке (DLLMain)?

#cpp #многопоточность #dll #cpp11


Приветствую. Есть некая функция, которая получает хеш-сумму файла (exe) и записывает
ее в файл. Она запущена в потоке, но приложение откуда она запускается - крашит. Хотелось
бы узнать как правильно запустить функцию в потоке, чтобы приложение не крашилось.

void initialize()
{
    string buffer;
    thread t(calclulateHash, ref(buffer));
    t.detach();
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch (fdwReason)
    {
        case DLL_PROCESS_ATTACH:
        {
            initialize();
            break;
        }
    }
    return true;
}

    


Ответы

Ответ 1



Таким образом как вы делаете, делать нельзя. В DllMain нельзя запускать потоки (или проводить синхронизацию). Вынесите запуск потока в отдельную функцию и вызывайте её не из DllMain, а из другого места. Одной из возможных причин проблемы является синхронизация всех вызовов DllMain: каждый её вызов ждёт окончания других. Если вы запускаете поток, это приводит к попытке рекурсивного вызова с флагом DLL_THREAD_ATTACH (не PROCESS), что немедленно приводит к проблемам. DllMain — это специальная; очень ограниченная функция, принципы, распространяемые на обычные функции, тут не работают. Ну и интересна сигнатура функции calculateHash, конечно.

Ответ 2



Грубо говоря, локальная переменная string buffer; передается в поток по ссылке (благодаря std::ref): thread t(calclulateHash, ref(buffer)); каковой поток отсоединяется и выполняется и, как я понимаю, потом пытается писать в buffer t.detach(); которого уже нет, потому что функция, в которой он объявлен, давно закончилась... Зачем вам вообще считать нечто, что вы никак не используете?

воскресенье, 9 февраля 2020 г.

Выгрузить dll из кода dll

#delphi #winapi #dll


Есть dll для внедрения в уже работающий процесс, которая что-то делает, а затем самостоятельно
себя выгружает. Всё это происходит на самом этапе внедрения (при DLL_PROCESS_ATTACH).

library Test;

uses Winapi.Windows;

procedure DllMain(Reason: integer);
begin
  if Reason = DLL_PROCESS_ATTACH then
  begin
    // что-то делаем
    FreeLibraryAndExitThread(HInstance, 0); // выгружаемся
  end;
end;

begin
  DllProc := @DllMain;
  DllMain(DLL_PROCESS_ATTACH);
end.


Вероятно, при выгрузке происходит какая-то блокировка, в результате которой иногда
процесс наглухо зависает. Иногда этого не происходит, но корректно закрыть процесс
после этого всё равно не получается.

Пример:
1) Запускам, скажем, калькулятор
2) Внедряем в него эту dll (Process Hacker'ом, например)  

Дальше я смог поймать три типа поведения процесса:
а) Он намертво зависает
б) Перестаёт реагировать на закрытие крестиком
в) При закрытии основного окна остаётся висеть в списке процессов.

Несмотря на то, что dll себя удачно выгружает и даже завершает свой поток — она что-то
портит в процессе.

Если убрать из кода выгрузку, и делать её отдельно — всё работает как надо.

Вопрос: как избавиться от такого поведения с сохранением функциональности по самостоятельной
выгрузке dll?



UPD1: Если заменить FreeLibraryAndExitThread на FreeLibrary всё становиться ещё хуже:
процесс тут же падает с ошибкой BEX64.
    


Ответы

Ответ 1



DLLMain вызывается системой под общей блокировкой, поэтому существуют значитильные ограничения на выполнение чего-либо внутри этой функции. Warning There are significant limits on what you can safely do in a DLL entry point. See General Best Practices for specific Windows APIs that are unsafe to call in DllMain. If you need anything but the simplest initialization then do that in an initialization function for the DLL. You can require applications to call the initialization function after DllMain has run and before they call any other functions in the DLL. т.е. самый простой способ решить проблему - вызывать отдельную функцию инициализации из этой DLL, которую Вы можете экспортировать наравне со всеми другими функциями и структурами. Посмотрите так же перечень задач, которые нельзя выполнять в DllMain: Call LoadLibrary or LoadLibraryEx (either directly or indirectly). This can cause a deadlock or a crash. Call GetStringTypeA, GetStringTypeEx, or GetStringTypeW (either directly or indirectly). This can cause a deadlock or a crash. Synchronize with other threads. This can cause a deadlock. Acquire a synchronization object that is owned by code that is waiting to acquire the loader lock. This can cause a deadlock. Initialize COM threads by using CoInitializeEx. Under certain conditions, this function can call LoadLibraryEx. Call the registry functions. These functions are implemented in Advapi32.dll. If Advapi32.dll is not initialized before your DLL, the DLL can access uninitialized memory and cause the process to crash. Call CreateProcess. Creating a process can load another DLL. Call ExitThread. Exiting a thread during DLL detach can cause the loader lock to be acquired again, causing a deadlock or a crash. Call CreateThread. Creating a thread can work if you do not synchronize with other threads, but it is risky. Create a named pipe or other named object (Windows 2000 only). In Windows 2000, named objects are provided by the Terminal Services DLL. If this DLL is not initialized, calls to the DLL can cause the process to crash. Use the memory management function from the dynamic C Run-Time (CRT). If the CRT DLL is not initialized, calls to these functions can cause the process to crash. Call functions in User32.dll or Gdi32.dll. Some functions load another DLL, which may not be initialized. Use managed code. И перечень задач, которые разрешены в DllMain: Initialize static data structures and members at compile time. Create and initialize synchronization objects. Allocate memory and initialize dynamic data structures (avoiding the functions listed above.) Set up thread local storage (TLS). Open, read from, and write to files. Call functions in Kernel32.dll (except the functions that are listed above). Set global pointers to NULL, putting off the initialization of dynamic members. In Microsoft Windows Vista™, you can use the one-time initialization functions to ensure that a block of code is executed only once in a multithreaded environment. Подробности можно получить из General Best Practices Другой способ вызвать что-либо в DllMain из запрещенного списка выше: создать в DllMain поток, в котором и выполнить все необходимые действия. Поток обойдет глобальную блокировку уже за пределами DllMain, поэтому в нем можно выполнять код без ограничений. Тут тоже есть некоторые нюансы, подробности - в блоге ms: Does creating a thread from DllMain deadlock or doesn’t it?

среда, 5 февраля 2020 г.

Куда положить dll?

#dll


Я написал программу. На других компьютерах требует msvcr100d.dll.
Я его скачал. Вот вопрос: куда его надо теперь положить?    


Ответы

Ответ 1



Правильный подход - установить пакет: Microsoft Visual C++ 2010 Redistributable Package (x86). Или добавить его установку в свой собственный проект.

Ответ 2



Если вы действительно уже написали вашу программу, то вам в первую очередь следует откомпилировать ее начистовую, т.е. в конфигурации Release, чтобы она использовала "чистовую" версию стандартной библиотеки. В частности: msvcr100.dll, которая поставляется в рамках Microsoft Visual C++ 2010 Redistributable Package. Ни о какой msvcr100d.dll речи быть не может и скачивать ее вы ниоткуда не должны. Если вашей программе понадобился msvcr100d.dll, значит вы занимаетесь какой-то ерундой. Прекратите ваши бессмысленные попытки запускать вашу программу "на других компьютерах" и начните с правильной компиляции.

Ответ 3



Если положить все нужные dll в папку с программой, то она, скорее всего, успешно запустится.

пятница, 31 января 2020 г.

Программа не запускается без dll

#cpp #net #dll


Написал программу на c++. В ней есть функции: запуск программы, добавление записи
в реестр, цикл и пара функций, а также несколько поключенных заголовков. 

Как мне запустить уже скомпилированную программу практически на нулевом Windows (выдается
ошибка о не найденом dll файле, возможно ли этот dll вместе с программой скрепить или
вшить в нее) или как мне, допустим, с .net framework 4.0 опустить требования программы,
скажем, до .net framework 3.0?
    


Ответы

Ответ 1



Проще всего - у вас явно никакого .NET не видно - собрать программу со статическими библиотеками и не мучиться. В командной строке - ключик /MT, в проекте - меню Проект - Свойства - Создание кода - Библиотека времени выполнения - Многопоточная. Тогда все необходимое из runtime-библиотек VC++ будет включено в код программы, и никакие DLL тянуть не потребуется. Размер EXE, понятно, будет побольше, чем при динамической компоновке. Еще - скажем, если программа должна работать на чем-то стареньком типа XP, и у вас точно не используется ничего нового из API - можно указать линковщику соответствующий параметр /VERSION - а то последние версии VC++ ставят по умолчанию не ниже Windows 7.

Ответ 2



MSVCP140D.dll является частью того, что называется Microsoft Visual C++ Run-Time Redistributable. Правильное решение этой проблемы - требовать установки этой штуки на пользовательском компьютере, или распространять вместе с программой ее установщик. Кстати, D в имени означает отладочную библиотеку. Есть подозрение, что библиотека без D и так присутствует на любой современной винде. Попробуйте скомпилить свою программу в режиме Release и затем запустите на другом компьютере.

Ответ 3



Если программа НЕ использует .net framework и не нуждается в нём, то можно так: Берёшь чистую виртуалку и копируешь на неё программу. Запускаешь, она падает с сообщением, в котором указана dll, которой её не хватило. Находишь эту dll у себя и кладёшь рядом с программой. запускаешь снова. Повторять до тех пор, пока программа не запустится. А когда запустится, у тебя будет папка с программой и пачкой dll - можно тащить куда угодно.

Функция из dll возвращает неверные значения Python

#python #delphi #dll #ctypes


Есть Dll(исходников нету), есть интерфейсный модуль к ней написанный на Delphi. Вызываю
функцию: 

function ProcessBonusListCountersPrint(vBonusCountersPrintQuery: TBonusCountersPrintQuery;
var vListCountersPrint: TListCountersPrint): Integer; stdcall;

type TBonusCountersPrintQuery = packed record
     Card: array[0..15] of char;

type TListCountersPrint = packed record         
     Lines: array[0..PACKET_COUNTERS_PR_MAX_LINE_COUNT - 1] of TCounterLine;

type TCounterLine = packed record
     LineNo: word;
     IsLast: byte;
     Num_Counter: word;
     N_Counter: array[0..9] of char;
     Value_Counter: Cardinal;


(Описание структур, функций беру из интерфейса)

На Python реализовал так:

from ctypes import *

class TBonusCountersPrintQuery(Structure):
_fields_ = [("Card", c_char * 15)]

class TListCountersPrint(Structure):
_fields_ = [("Lines", TCounterLine * 20)]

class TCounterLine(Structure):    
_fields_ = [("LineNo", c_int),
            ("IsLast", c_byte),
            ("Num_Counter", c_int),
            ("N_Counter", c_char * 11),
            ("Value_Counter", c_int)]

packet_counters_pr_max_line_count = 20;
i = 0;

ProcessBonusListCountersPrint = libc.ProcessBonusListCountersPrint

ProcessBonusListCountersPrint.argtype = [TBonusCountersPrintQuery, POINTER(TListCountersPrint)]
ProcessBonusListCountersPrint.restype = c_int32

vBonusCountersPrintQuery = TBonusCountersPrintQuery()
vBonusCountersPrintQuery.Card = b'123456798'

vListCountersPrint = TListCountersPrint()

res = ProcessBonusListCountersPrint(vBonusCountersPrintQuery, byref(vListCountersPrint))
print('ProcessBonusListCountersPrint', res)

for i in range(packet_counters_pr_max_line_count):
    if vListCountersPrint.Lines[i].IsLast == 1:
        break
    print(vListCountersPrint.Lines[i].Num_Counter, vListCountersPrint.Lines[i].N_Counter.decode('cp1251'),
vListCountersPrint.Lines[i].Value_Counter)


vListCountersPrint.Lines[i].N_Counter - возвращается правильное значение, а вот числовые
значения из этой структуры все неверные. Числовые типы все пробовал, ни один не дал
даже похожего результата.
Вопрос: как получить нужные мне данные?(в среде Borland Delphi 7 все работает)
    


Ответы

Ответ 1



Структуры в Python надо объявлять с параметром _pack_ = 1, поскольку в Delphi они объявлены как packed. Неправильно объявлены поля Card (15 байт, хотя в Delphi - 16) и N_Counter (11 байт, хотя в Delphi - 10). В структуре TCounterLine в Python использованы неправильные типы для LineNo и Value_Counter (в Delphi word - это 2-х байтовое беззнаковое целое, в ctypes для этого есть тип c_ushort).

Ответ 2



Совместил ответ от zed и tonal. В итоге получилась такая структура: class TCounterLine(Structure): _pack_ = 1 _fields_ = [("LineNo", c_short), ("IsLast", c_byte), ("Num_Counter", c_short), ("N_Counter", c_char * 10), ("Value_Counter", c_int)] Всё заработало. Большое спасибо за помощь)

Ответ 3



Вот так будет правильно: class TCounterLine(Structure): _fields_ = [("LineNo", c_short), # LineNo: word; ("IsLast", c_byte), # IsLast: byte; ("Num_Counter", c_short), # Num_Counter: word; ("N_Counter", c_char * 10), # N_Counter: array[0..9] of char; ("Value_Counter", c_int)] # Value_Counter: Cardinal;

среда, 29 января 2020 г.

Подключить DLL

#c_sharp #wpf #dll


Пишу десктопное приложение на C#, которая по задумке должна подключать dll, уже будучи
скомпилированой и работающей.
Как это возможно реализовать? И возможно ли вообще :)    


Ответы

Ответ 1



Варианты предлагаю такие: Использовать MEF (как в ответе выше) Использовать MAF (как альтернатива, она может выгружать DLL налету) Использовать Prism Создать общую библиотеку с хорошо продуманным интерфейсом и классом (н-р InitPlugin); обязать ваши библиотеки иметь эту библиотеку и обращаться к нужным интерфейсам посредством Reflection. Для неуправляемого кода можно использовать очень интересную возможность: дело в том, что когда функция C# с атрибутом ImportDll обращается к библиотеке, она ищет ее в определенных местах (в системной папке и нек других), но если не найдет, то обязательно также посмотрит в CurrentDirectory. Устанавливая Environment.CurrentDirectory на нужные каталоги, можно добиться системы плагинов на неуправляемом уровне. Последний который приходит в голову, это компилить какие-нибудь алгоритмы прямо на лету через CSharp Compiler.

Ответ 2



Рассматривайте ваши бибилиотеки как "плагины" или расширения основной программы. Начиная с 4 версии фреймворка в платформу .NET включен Managed Extensibility Framework Платформа Managed Extensibility Framework, или MEF, – это библиотека для создания простых расширяемых приложений. Она позволяет разработчикам приложений находить и использовать расширения без каких-либо настроек. Кроме того, дает разработчикам расширений возможность легко инкапсулировать код и избежать использования ненадежных жестких зависимостей. MEF не только позволяет использовать зависимости повторно, но и дает возможности применять их в различных приложениях.

пятница, 24 января 2020 г.

Serial Communication DLL

#c_sharp #cpp #dll #serial


Пытаюсь написать dll на С++ для связи с com-портами, где dll будет использоваться
через DllImport в С#.

Код пишется на примере msdn

Для начала я попытался использовать данный пример в консольной аппликации :

static SerialPort^ _serialPort;

static void Main()

    _serialPort = gcnew SerialPort("COM9");

    _serialPort->ReadTimeout = 500;
    _serialPort->WriteTimeout = 500;

    _serialPort->Open();

    _serialPort->WriteLine(String::Format("test"));

    _serialPort->Close();


все замечательно работает (вылавливаю сообщение test через putty), но при попытке
использовать тот же код, но уже через dll: 

static void Main()
{

    SerialPort^ _serialPort = gcnew SerialPort("COM9");

    _serialPort->ReadTimeout = 500;
    _serialPort->WriteTimeout = 500;

    _serialPort->Open();

    _serialPort->WriteLine(String::Format("test"));

    _serialPort->Close();
}

int pOpen()
{
    PortChat::Main();
    return 1;
}


прилетает вот такое вот зло:


  System.UnauthorizedAccessException: Access to the port 'COM9' is
  denied.


Собственно, что я делаю не так? (и вполне вероятно, что я делаю не так ВСЁ из-за
не понимания) 

Стоит добавить что часть C# (которая вызывает dll) работает через JavaScript.
То есть все это несчастье запускается через default.html.

Огромное спасибо.
    


Ответы

Ответ 1



Вы пробовали запускать приложение от имени администратора? Может имеет смысл попробовать реализовать инициализацию последовательного порта средствами winApi (FileRead), там можно и параметры безопасности использовать. Вообще, лучше (наверно) использовать стандартный класс последовательного порта, присутствующий в .NET.

Ответ 2



Проверьте может при компиляции dll "COM9" изменяется (бывают проблемы с кодировкой). Еще как вариант запустите приложение под администратором.

Список DLL в x64 Windows для wow64

#windows #dll


Есть 32 битный процесс в 64 битной системе.
Пытаюсь получить список DLL через:


PEB и Module32Next



  C:\Windows\SYSTEM32\ntdll.dll
  C:\Windows\SYSTEM32\kernel32.dll
  C:\Windows\SYSTEM32\kernelbase.dll
  C:\Windows\SYSTEM32\user32.dll
  C:\Windows\SYSTEM32\gdi32.dll
  C:\Windows\SYSTEM32\msvcr100.dll
  C:\Windows\SYSTEM32\imm32.dll



Process Explorer



  C:\Windows\SysWOW64\bcryptprimitives.dll
  C:\Windows\SysWOW64\combase.dll C:\Windows\SysWOW64\cryptbase.dll
  C:\Windows\SysWOW64\dwmapi.dll C:\Windows\SysWOW64\gdi32.dll
  C:\Windows\SysWOW64\imm32.dll C:\Windows\SysWOW64\kernel.appcore.dll
  C:\Windows\SysWOW64\kernel32.dll C:\Windows\SysWOW64\KernelBase.dll
  C:\Windows\System32\locale.nls C:\Windows\SysWOW64\msctf.dll
  C:\Users\1\Desktop\msvcr100.dll C:\Windows\SysWOW64\msvcrt.dll
  C:\Windows\SysWOW64\ntdll.dll C:\Windows\System32\ntdll.dll
  C:\Windows\SysWOW64\rpcrt4.dll C:\Windows\SysWOW64\sechost.dll
  C:\Windows\SysWOW64\SHCore.dll
  C:\Windows\Globalization\Sorting\SortDefault.nls
  C:\Windows\SysWOW64\sspicli.dll C:\Windows\Fonts\StaticCache.dat
  C:\Users\1\Desktop\test.exe C:\Windows\SysWOW64\user32.dll
  C:\Windows\SysWOW64\ru-RU\user32.dll.mui
  C:\Windows\SysWOW64\uxtheme.dll C:\Windows\System32\wow64.dll
  C:\Windows\System32\wow64cpu.dll C:\Windows\System32\wow64win.dll


Почему в 1 случае "C:\Windows\SYSTEM32\", а не C:\Windows\SysWOW64.
Запрашивается ведь из 32 битного(WOW64) процесса для себя же.

HANDLE h;
PEB p;
PROCESS_BASIC_INFORMATION s;
DWORD w=0;
HMODULE hMsi;
PLDR_MODULE curr;
PLDR_MODULE b;
DWORD adr;
BYTE *bfv;
long sz;
DWORD r;
HANDLE hf;
MODULEENTRY32 pf;


hMsi=LoadLibrary("ntdll.dll");
NtQueryInformationProcess=(NtQueryInformationProcessQ)GetProcAddress(hMsi,"NtQueryInformationProcess");


h=CreateToolhelp32Snapshot(TH32CS_SNAPMODULE,GetCurrentProcessId());
ZeroMemory(&pf,sizeof(pf));
pf.dwSize=sizeof(pf);
Module32First(h,&pf);
for(;;)
{
    ZeroMemory(&pf,sizeof(pf));
    pf.dwSize=sizeof(pf);
    w=Module32Next(h,&pf);
    printf("%s \n",pf.szExePath);
    if( w==0 ) break;
}

MessageBox(0,0,0,1);


ZeroMemory(&s,sizeof(s));
h=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,0,GetCurrentProcessId());
if( h>0 )
{
    if( NtQueryInformationProcess(h,ProcessBasicInformation,&s,sizeof(s),&w)==0 )
    {
        // if( GetProcAddress(LoadLibrary("kernel32.dll"),"IsWow64Process")==0 )
        // {
            ZeroMemory(&p,sizeof(p));
            ReadProcessMemory(h,s.PebBaseAddress,&p,sizeof(p),&w);
            if( w>0 )
            {
                curr=(PLDR_MODULE)p.Ldr->InMemoryOrderModuleList.Flink;
                curr=(PLDR_MODULE)((DWORD)curr-sizeof(LIST_ENTRY));
                b=(PLDR_MODULE)&p.Ldr->InMemoryOrderModuleList;
                b=(PLDR_MODULE)((DWORD)b-sizeof(LIST_ENTRY));

                while(curr!=b)
                {
                    printf("%p \n",curr);
                    wprintf(L"%s \n",curr->FullDllName.Buffer);
                    printf(" \n");

                    curr=(PLDR_MODULE)curr->InMemoryOrderModuleList.Flink;
                    curr=(PLDR_MODULE)((DWORD)curr-sizeof(LIST_ENTRY));
                }
            }
        }
    }
}

    


Ответы

Ответ 1



Почему в 1 случае "C:\Windows\SYSTEM32\", а не C:\Windows\SysWOW64. Запрашивается ведь из 32 битного(WOW64) процесса для себя же. Именно потому что запрашивается из 32-х битного процесса. Wow64 использует перенаправление реестра и файловой системы для того чтобы все 32-х битные приложения нормально запускались на 64-х битных системах, если в них вдруг жёстко прописаны системные пути. Если вы хотите получить реальные пути файлов на 64-х битной системе, то надо либо использовать 64-х битное приложение либо искать пути обхода виртуализации (если они вообще существуют).

Как загрузить .dll в visual studio

#cpp #visual_studio #dll #import


Нужно написать глобальный хук на клавиатуру и мышь. Создал проект dll, собрал. В
исполняемом проекта в свойствах: Linker->Advanced->Import Library прописал абсолютный
путь(скопировал из файловой системы) к .dll файлу полученному в результате сборки.

Далее такой код dllKeyboard = LoadLibraryA("MouseAndKeyboard.dll"); кидает ошибку
126 - Указанный модуль не может быть найден. Что делать?
    


Ответы

Ответ 1



"Linker->Advanced->Import Library" - настройка студии. К путям поиска DLL, которые используются при вызове LoadLibrary(), эта настройка отношения не имеет. Указание полного пути к DLL при загрузке либы определенно должно решить вашу проблему.

четверг, 23 января 2020 г.

Как прикрепить патч для dll к проекту?

#sdl #opengl #cpp #dll


Я написал приложение на SDL + OPENGL + C++ в CodeBlocks. Обнаружил, что при запуске
исполняемого файла, скомпилированного в релизе (при запуске в релизе в самой среде
всё отлично работает) приложение некоторые .png файлы не загружает (а именно png-24)
и выдаёт в файл для ошибок (stderr) "libpng warning: Interlace handling should be turned
on when using png_ read_ image". Как выяснилось, sdl_ image использует такую libpng(а
именно libpng15-15.dll, другие я пробовал, sdl_image вроде требует именно эту либу),
в которой функция png_read_image имеет ошибку. Я долго искал решение (png-24 нужны),
качал вроде бы более новые версии libpng, но всё тщетно. Нашёл патч, который должен
фиксить эту ошибку:
--- libpng-1.5.0/pngread.c.ark  2011-01-14 12:27:23.440018507 +0100
+++ libpng-1.5.0/pngread.c  2011-01-14 12:28:02.866685173 +0100
@@ -841,7 +841,7 @@ png_read_image(png_structp png_ptr, png_
    }
    else
    {
-      if (!(png_ptr->transformations & PNG_INTERLACE))
+      if (png_ptr->interlaced && !(png_ptr->transformations & PNG_INTERLACE))
   {
      /* Caller called png_start_read_image or png_read_update_info without
       * first turning on the PNG_INTERLACE transform.  We can fix this here,

но не пойму? как его прицепить к  проекту. Скомпилировать свою libpng из исходников
пока не получается (очень не хочется этим заниматься). Помогите пожалуйста!    


Ответы

Ответ 1



Вам придётся скомпилировать libpng. Патч предназначен для исходников, к исходникам его и надо применять. Вы не сможете так просто применить патч исходников для бинарника. В качестве ненадёжной альтернативы, можно попробовать дизассемблировать код libpng, найти нужную функцию, и применить патч вручную. Однако, это имеет право не сработать -- например, если оптимизатор заинлайнил кое-где вызов этой функции. Короче говоря, легче скомпилировать. Кстати, может быть, легче будет уговорить sdl_image использовать другую версию libpng?

Ответ 2



Итак, нашёлся ответ. Собственно я нашёл решение только по другим странностям, когда релиз и дебаг версии начали работать совсем не так, как скомпилированные .ехе. начали вылетать ошибки на простейших функциях opengl и сам .ехе вариант очень тормозил. Оказывается я в начале сунул какую-то кривую версию библиотеки opengl в папку и компилятор работал с нормальной, а вот ехе с кривой.

Как при наличии отладочных символов только своей dll получить её переменные из дампа памяти приложения?

#visual_studio #dll #отладка #dump


Есть моя DLL библиотека (проект Visual Studio 2013). Ее используют чужие программы.
Во время работы этих программ было сохранено несколько полных дампов память процессов
этих программ.
Я открываю эти дампы в моем Visual Studio проекте. Отладочные символы я имею только
для моей DLL. При этом, если во время сохранения дампа исполнялся код одной из функций
моей DLL, то видно все символы в исходном коде, место текущего исполнения итд. Но если,
исполнялся другой код, я никаких данных не вижу и посмотреть текущее значение переменных
моей DLL не могу. Локальные переменные ясное дело недоступны, так как их нет, но и
статические данные тоже не видны.  

Как просматривать данные моей DLL?
    


Ответы

Ответ 1



Не уверен, можно ли это сделать через студию. Но точно можно через WinDBG: Открыть дамп. Включить ссылки в выводе: .prefer_dml 1 Прописать путь к символам: .sympath+ srv* Загрузить SOS (пусть исправить на соответствующий версии и платформе) для 4.0: .loadby sos clr для <4.0 .loadby sos mscorwks или по полному пути .load C:\Windows\Microsoft.NET\Framework64\v4.0.30319\SOS.dll Найти EEClass для того типа, для которого хочется посмотреть статические поля > !name2ee mscorlib.dll System.Console Module: 0000064278854000 (mscorlib.dll) Token: 0x000000000200008b MethodTable: 00000642788c8d10 EEClass: 0000064278a271a8 Name: System.Console Посмотреть статические поля: > !dumpclass 0000064278a271a8 Class Name: System.Console mdToken: 000000000200008b (C:\WINDOWS\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll) Parent Class: 00000642788c0c30 Module: 0000064278854000 Method Table: 00000642788c8d10 Vtable Slots: 4 Total Method Slots: 78 Class Attributes: 100181 Abstract, NumInstanceFields: 0 NumStaticFields: d MT Field Offset Type VT Attr Value Name 00000642788f5aa0 40002ae d8 System.IO.TextReader 0 shared static _in Дальше перемещаться кликами по адресам объектов - в режиме DML по клику будет выполнятся соответствующая команда для дампа объекта. По настройке WinDBG есть отличный мануал Debugging Managed Code Using the Windows Debugger.

среда, 22 января 2020 г.

Получение картинки с веб камеры

#c_sharp #dll #web_camera


Подскажете библиотеку для работы с изображением веб камеры C#.
Пожалуйста приведите примеры кода или статьи уроков по работе с библиотекой.
    


Ответы

Ответ 1



Нашел код в несколько строк. using Emgu.CV; Capture capture = new Capture(); //create a camera captue Bitmap image = capture.QueryFrame().Bitmap; //take a picture pictureBox.Image = image;