Обновить
58

Пользователь

11
Подписчики
Отправить сообщение

Это просто у меня депрессия, или статья состоит из двух типов новостей: печальных новостей и якобы хороших новостей, от которых гнетущий эффект не меньше, чем от первых?

В байт-коде не нужно пересчитывать все переходы внутри программы,

С чего это ради? Эти слова косвенно говорят о том, что вы не понимаете или неправильно понимаете то, как устроен P-код VBVM и его выполнение.

Точно так же, как и в машинном коде, в P-коде все условные и безусловные переходы внутри процедуры нужно пересчитывать, если что-то в ней перекраивалось. С P-кодом даже больше нужно сделать: если в x86-инструкциях условных и безусловных переходов операнд закодирован как относительное смещение целевого адреса (относительно начала следующей инструкции — следующей по отношению к Jxx), так что целый блок x86-инструкций можно двигать, и все Jxx инсрукции в нём будут position independant, если не ведут за пределы блока), что в P-code все jump destination указываются не относительно следующей инструкции, а относительно начала процедуры (первой P-code инструкции процедуры). И VB IDE всё это делает при перекраивании: пересчитывает, правит.

А если вы переменную добавили в выполняющейся подпрограмме в машинном коде - ещё и фрейм стека перестраивать, и всё над ним двигать?

Всё то же самое актуально и для P-кода. Виртуальная машина VB стековая, буквально для всех действий сначала нужно что-то положить на стек: для вычисления выражений, сколько угодно сложным бы оно ни было, операнды кладутся на стек а затем вызываются инструкции, соответствующие операциям (и результат после выполнения такой инструкции тоже остаётся на вершине стека), при вызове процедур параметры складываются на стек и так далее. При этом какой-то свой отдельный обособленный стек не используется. Используется основной стек = процессорный стек, с верхушкой в ESP.

И таким образом добавление новой локальной переменной в уже выполняющейся в данной момент процедуре потребовалало бы перекраивания фрейма для вклинивания туда новой локальной переменной.

И тем не менее, VB IDE позволяет остановиться прямо внутри процедуры, добавить несколько новых локальных переменных, вставив их объявления в код, и продолжить выполнение (помимо добавления новых переменных можно добавить и код, произвольным образом их использующий).

Делать это можно и в том числе, если, например, мы стопнулись внутри рекурсии на каком-нибудь 10-ом уровне вложенности. Так что у нас не только процедура «Test» прямо сейчас находится в середине своего выполнения, но и вызвана она из Test, а Test вызвана из Test — и так 10 уровней. И добавлять новые локальные переменные — можно. И они появятся в каждом фрейме этой рекурсии с 10 уровнями вложенности.

Для этого VB IDE не перекраивает стековый фрейм (или стековые фреймы, в случае с рекурсией). Всё гораздо проще. Для этого просто изначально в фрейме есть резервное место на случай добавления новых переменных в процессе отладки. Примерно 150 байт на случай, если внезапно захочется приостановить выполнение и понавставлять везде новых локальных переменных. Это очень большой запас: 30 с чем-то локальных переменных типа Long/String/Object/Single. Я ни разу при правке кода по живому не упирался в этот лимит: обычно за раз хочется добавить переменных никак не больше, чем пальцев на руке.

И в Delphi тоже так могли бы сделать. Сколько держать резерва на случай появления новых локальных переменных в процессе работы программы могли бы сделать кастомизируемым — для тех, у кого в планах запускать оргромные по вложенности рекурсии и есть опасение, что стека не хватит.

Отдельно хочется сказать, что от такой локализации интерфейса VB кровь из глаз идёт.

Особенно мне нравится вкладка «Делать»...

Да речь не о визуальных компонентах, а о просто библиотеках, реализующих всякие алгоритмы и обработки.

Рынок именно что компонентов для формошлёпства действительно был. А Python-то выстрелил не формошлёпством.

ChatGPT не далёк от истины. Каждой P-code-ной инструкции соответствует кусочек машинного кода виртуальной машины, имплементирующий функциональность этой инструкции. Эти кусочки очень компактны, состоят всего из нескольких машинных инструкций, они написаны на MASM-е, то есть это даже не процедуры/функции — у них нет прологов, у них нет эпилогов, то есть нет сохранения регистров (которые соглашение о вызове предписывает оставлять нетронутыми), нет восстановления регистров в конце, нет резрвирования места в стеке под локальные переменные. В них есть только сама суть, так как они написаны на ассемблере. Поэтому они очень компактны.

И поскольку они очень компактны (а также из-за того, что для реально одинаковых поддействий из таких кусочков всё-таки вызываются классические процеды), реализация самых «популярных» P-кодных инструкций очень быстро попадает в кеш процессора. P-кодый код, если там выполняюся одни и те же процедуры VB-проекта, или много циклов, попадают в кеш данных процессора.

Таким образом, для процессора выполнение большей части VB-кода превращается в исполнение уже очень хорошо знакомых (с точки зрения кеша) макро-блоков.

 (иерархическая база данных без индексов) в 1995-м ключик и загрузить нужную библиотеку - это было быстро?

Почему это без индексов? Там хеш-таблиц. Поиск в реестре весьма быстрый.

И ресурсоёмкое, и неудобное, 

Что именно ресурсоёмкое?

Если ресурсоёмким был поиск в реестре (и на этом всё), то во-первых мы выяснили, что была альтернатива. То что альтернатива не была распространённой — это не аргумент, потому что это социальный фактор, а мы о технической стороне говорим. К социальным факторам его сводить не надо, потому что сам первоначальный вопрос взывает к социальному фактору: с чего ради один подход взлетел, а второй не взлетел.

Вдобавок к этому, если кому-то надо было создавать сотни тысяч объектов в секунду, то достаточно было просто 1 раз получить объект-фабрику и потом у неё вызывать метод CreateInstance, а не сотни тысяч раз искать в реестре. Более того, я почти уверен, что внутри OLEAUT32.DLL был кеш на этот случай.

Как минимум, там напрочь отсутствует взаимодействие с какими-либо сервисами ОС, кроме файловой системы.

Ха-ха, как будто файловая система это какая-то мелочь. Если бы файловая система была невесомо быстрой, никто бы не изобретал реестр, а все опции, настройки и прочее, что обычно хранится в реестре, просто распихали бы по микроскопическим файлам файловой системы, которая такая же иерархическая, как файлы. Но нет, сделали реестр.

Да файловая система куда большее зло.

VB тогда не умел в компиляцию, как я уже писал, и не будет уметь ещё шесть лет,

Он умел, просто это компиляция не в машинный код, а в бай-код. В самом слове компиляция нет никакого намёка на машинный код, поэтому самая настоящая манипуляция — уклоняться называть компиляцию в байт-код собственно компиляцией. Не нативность цепочки инструкций по отношению к CPU даёт процессу получения этой цепочки по праву называться компиляцией, а тот факт исходный код был разобран, его синтаксис проверен, его логическая структура (вложенность блоков) верифицирована, связанность идентификаторов верифицирована, замысел программиста был переведён в последовательность макро-операций, при этом выполнены все вычисления, которые можно выполнить на стадии компиляции, элиминированы пути исполнения, который никогда не достижимы, устранены избыточные макро-операции (вроде перекладываний данных туда сюда). Так что я не принимаю больше провокационных высказываний, что он не умел в компиляцию. Я люблю native code, но в контексте данного дискурса свет на нём клином не сошёлся.

Но ко всему прочему, можно подумать, что Python тогда умел в компилцию в native code?

Там это делалось просто "New ActiveX", и дальше просто как обычный элемент управления писать.

Попробуйте написать на Delphi ActiveX-сервер с классом, экземпляр которого должен олицетворять математическое выражение или математическую функцию (от одного или нескольких переменны) и притворяться этой функцией, и чтобы это можно было использовать из скриптового языка, например из VBScript, примерно вот так:

Set math_func = CreateObject("HabrDiscussionServer.MathFunc")
math_func.Expression = "f(g,x) = (sin(g) + sqr(x)) / (g*x)"

a = math_func(1,2)
b = math_func(3.141, 25)

Чтобы не тратить время и не смещать акцент на написание эвалюатора матвыражений, можете вообще не писать эту часть, а сделать заглушку, которая будет просто возвращать сумму всех аргументов вместо вычисления user-defined выражения. Вопрос не в том, где проще написать эвалюатор, а именно в ActiveX-специфичных вещах.

Сравним компактность и простоту реализаций на VB и на Delphi.

Не было, т.к. это всё-таки не интерпретатор :Р.

Так и VB IDE не интерпретировала исходный текст, а выполняла заранее скомпилированный байт-код, но при внесении правок в исходный код на живую в процессе выполнения перекраивала этот байт код, если это было в принципе осуществимо.

При желании авторы Delphi могли бы сделать такое же перекраивание машинного кода. Например, если я взял и вырезать строчку с вызовом функции в процессе пошаговой отладки программы, взять и заNOP-ить её на уровне машинного кода. Или если я что-то добавил, пропатчить код с помщью jmp и внести недостающие инструкции.

Нечто с очень ресурсозатратным связыванием

Господи... ужас... шок... фейспалм.

Неужели вслед за статьёй что QB и VB не был интерпретируемым языком, а генерировали настоящий полноценный машинный код, нужно писать следующую, с разоблачением новой байки?

С другой стороны, у вашего ответа есть ещё и второй дно: даже если бы всё что вы пишите, было правдой (а оно не было), то удивительно, что в сравнении с Python у последнего всё так же, если не хуже.

Но вернёмся к очень ресурсозатратному связыванию. Я не уверен, что именно вы понимаете под связыванием, но, в любом случае, «очень ресурсозатратное связывание» существует только в вашем воображении.

  • Если вы про то связывание, которое имеет место в момент вызова метода у объекта, то вопреки тому, что вы там себе придумали, вызов любого метода COM-объекта НИЧЕМ НЕ ОТЛИЧАЕТСЯ от вызова метода C++-класса, объявленного с ключевым словом virtual, за исключением использования соглашения stdcall вместо thiscall (указатель на this передаётся не в регистре ECX, а первым параметром на стеке).

    То есть буквально Something.Foo() в VB ничем не отличается от Something->Foo в С++, при условии, что Foo объявлена как
    virtual __stdcall returntypename Foo() = 0;

    На выходе будет сгенерирован код типа
    mov eax, dword [Something]
    mov ecx, dword [eax] ; Получаем адрес vtbl
    push eax ; Пушим this неявным первым аргументом
    call [ecx+nnnnn] ; Вызываем метод foo, где nnnn — смещение в vtbl


    Ну и каким боком это связывание очень ресурсозатратное или какое-то сложное?

    Это называется поздним связыванием в мире C++ (в противовес раннему связыванию, происходящему при линковке, если метод класса был не виртуальным), но в мире COM это наоборот называется ранним связыванием. А есть ещё и позднее связывание в мире COM, и вы наверное имели в виду его сложность и ресурсоёмкость. Только вот использование позднего связывания — это фича, опция, а не обязанность. Не хотите: можете не использовать его со стороны вызывающего кода (на VB). Не хотите — можете не имплементировать его на стороне реализации COM-объекта. Не обязательно вообще реализовывать механизм позднего связывания. Это в технологии ActiveX/OLE Automation ваш coclass должен быть dual-ным и поддерживать IDispatch и disp-интерфейсы для обычных COM-интерфейсов. Но никто не заставляет вас делать поддержку всего этого, если вам не нужно или обременительно.

    Ярчайший пример: библиотеки DirectX, Direct3D того времени (6, 7, 8, 9). Условно возьмём библиотеки d3d9.dll —  всё взаимодействие с Direct3D осуществляется через COM-интерфейсы, через работу с этими объектами и вызовы их методов. Это библиотека написана вообще без прицела на то, чтобы её использовали из VB. Создатели Half-Life 2, например, писали не на VB и использовали Direct3D, равно как и создатели сотен других игр.

    Тем не менее, использовать её VB легко и просто, равно как и любую другую подобную библиотека, будь она написана в том же духе.

  • Если же под связыванием подразумевается то, что Microsoft называет термином «активация» — то есть установление связи между COM-клиентом (вызывающей стороной) и COM-серверов, то и здесь всё не так, как вам хочется думать.

    В рамках COM не установлено и не регламентируется, как вы найдёте DLL-библиотеку, реализующую COM-объект, как с ней провзаимодействуете и как получите указатель на COM-объект. Как хотите, так и делайте.

    Это в рамках технологии ActiveX, чтобы не было бардака и разнобоя, установили единый концепт и единый способ (чтобы серверы регистрировали в реестре свои классы по их уникальным идентификаторам CLSID, а также регистрировали маппинг между человеко-читаемыми идентификаторами ProgId и 128-битными числовыми CLSID-ами, а также редактировали бы для серверов местоположение TLB и т.д. Просто чтобы каждый программист не изобретал велосипед и чтобы все программы использовали единый отлаженный механизм (подразумевающий поиск в реестре, который вы конечно же делаете не сами).

    Но это, опять же, как и в случае с поздним связыванием через IDispatch, по вашему желанию. Не хотите — не надо. Вот библиотека d3d9.dll не зарегистрирована в реестре. Ей не нужно регистрироваться в реестре. Она просто лежит в system32 и экспортирует обычную функцию Direct3DCreate9(). Вы импортируете эту функцию, вызываете, и вуаля — увас указатель на COM-объект. Точнее указатель на COM-интерфейс IDirect3D9 некоего объекта, олицетворяющего всю библиотеку Direct3D. Работаете сним, вызывайте его методы, и на этом будет построено всё ваше взаимодействие с Direct3D.

    Для свой собственной библиотеки вы можете делать то же самое. Экспортируйте функцию, которая вернёт корневой объект вашей библиотеки, а вызыващая сторона пусть дёрнет эту функцию и дальше работает с корневым объектом. Вы можете назвать функцию как угодно. Вы можете назвать библиотеку как угодно. Вы можете положить её куда угодно (вовсе не обязательно класть её в system32). Лишь бы вызывающая сторона могла дёрнуть из неё экспортируемую функцию.

    Но вы можете пойти дальше и вместо этой дикости воспользоваться стандартным унифицированным хорошо отлаженным механизмом. Зарегистрировать библиотеку в реестре. Точнее в реестре регистируется не только сама библиотека, сколько классы, в ней реализованные. Тогда вам не нужно отдельно документировать, как достучаться до вашей библиотеки и какие функции импортировать и вызывать, чтобы породить экземпляр какого-нибудь класса или получить корневой объект какой-то иерархии объектов. Все приложения будут использовать одинаковый механизм для создания ваших классов, в том числе их можно будет создавать и из скриптовых языков, а если объекты поддерживают ещё и IDispatch — можно будет не только создавать, но и вызывать у них методы.

    Но и тут можно пойти дальше. Я не понимаю, с чего ради способ этой активации считается каким-то ресурсоёмким.

    Система дарует вам функцию CoCreateInstance, как бы говоря, что теперь вы можете породить экземпляр любого класса, зная только уникальный идентификатор класса. Вам не нужно заботиться о том, где найти DLL-файл, загружать его, заботиться о том, какую функцию там импортировать и как её вызывать. Все стандартизированно, всё одинаково у всех библиотек и вам не нужно ничего делать самому.

    Для этого системная реализация CoCreateInstance делает пару простых вещей:
    1. Вызывает CoGetClassObject, чтобы получить ссылку на объект, олицетворяющий сам класс (экземпляр которого вы намереваетесь создать)
    2. Запрашивает у этого объекта интерфейс IClassFactory и у него вызывает метод CreateInstance.

    Только и всего. Сама же CoGetClassObject должна как-то находить DLL-библиотеку, внутри которой реализован не только сам класс, но и отдельный вспомогательный объект, олицетворяющий этот класс. Для этого CoGetClassObject делает несколкьо простых вещей:
    1. Ищет в реестре по предоставленному уникальному идентификатору класса ключик, содержащий полный путь к DLL-файлу.
    2. Вызывает LoadLibrary(), чтобы загрузить этот DLL-файл.
    3. У загруженной DLL вызывает экспортируемую оттуда функцию DllGetClassObject — и возвращает ссылку на объект, который отдала DLL-библиотека.

    Ничего тут нет ресурсоёмкого. В отличие от работы с обычной DLL-библиотекой, пусть даже не COM-библиотекой, разница только в том, что имя/расположение DLL-файла не жёстко захардкожено, а ищется в реестре, а затем, вызывается конкретно определённая функция, чтобы через неё выйти на фабрику класса и запросить создания нового экземпляра нового класса.

    В итоге создание нового экземпляра это:
    1. Получение полного имени файла из реестра.
    2. hmod = LoadLibrary(..._
    3. funcptr_GetClassObject = GetProcAddress(hmod, "DllGetClassObject")
    4. IClassFactory* cf = funcptr_GetClassObject( ... CLSID ...)
    5. cf->CreateInstance(...)

    И всё это обычные вызовы в духе Си/Си++.

    Я уверен на миллион, что это менее ресурсоёмко, чем вещи, которые происходят под капотом Python, когда он встречает строку
    from fooo.baar.baaz import yetanothershit

    Так что слова о том, что там делается «нечто очень ресурсозатратное» вообще не в тему.

    Но второй упрёк был в том, что первый пункт этого списка требует, чтобы в реестре были соответствующие ключики (чтобы по CLSID найти DLL файл). В общем-то да, требует, и это логичная мера, придуманная Microsoft, чтобы побороть DLL hell. Не понятно, почему нужно бояться регистрации DLL в файле.

    НО ЕСЛИ ВАМ ПО КАКОЙ-ТО ПРИЧИНЕ НЕ НРАВИТСЯ РЕГИСТРАЦИЯ В РЕЕСТРЕ — не делайте её. Механизм порождения нового экземпляра класса полностью документирован. Все вот эти шаги из 5-пунктного списка выше полностью были документированы. Если вы хотите использовать CoCreateInstance для быстрого и лёгкого создания нового объекта, то ей нужно, чтобы в реестре был ключик. Но если вам религиозные соображения, внутренние страхи или другие объективные факторы не позволяют прибегнуть к этой регистрации в реестре, просто делайте сами вручную эти 5 действий, только в качестве первого пункта вместо поиска в реестре делайте что-то другое. Напишите свой вариант CoCreateInstance, которая вместо поиска нужной DLL в реестре ищет её где-то в ином месте. В ini-файле, в файле MyCoolAppLibrariesPaths.txt, либо пусть там будет захардкожено имя библиотеки, а библиотека лежит рядом с программой. И всё, проблема решена.

    Для вашего удобства и искоренения проблемы DLL hell вам предложили способ с реестром, с централизованным местом регистрации всех ActiveX-серверов, но если вам это не нравится, порождайте объекты по своему, как вам нравится. Механизм порождения объектов не засекречен и документирован.

    Но есть вариант ещё покруче. Сколько лет как вышла Windows XP? Начиная с Windows XP придуман способ показать COM, в каком же DLL-файле живёт тот или иной класс (по его CLSID) не прибегая к созданию каких бы то ни было ключей в реестре. А значит не нужен доступен в реестр, не нужна инсталляция, не нужны админские права (они в общем-то и для регистрации в реестре не особо нуждны, потому что в HKEY_CURRENT_USER есть свой под раздел profile-specific регистраций.

    Суть способа — манифесты. Вы просто делаете XML-манифест и либо кладёте его отдельным файлом рядом со своим приложением, либо прямо в ресурсы EXE-файла его вшиваете. В манифесте нехитрыми XML-нодами указываете, что вам известны вот такие-то CLSID-ы и что искать их нужно в таких-то DLL-файлах.

    Всё, начиная с Windows XP, вы можете использовать стандартный способ порождения экземпляров желаемого класса по его CLSID-у (либо ProgId-у), но при CoCreateInstance не пойдёт ничего искать в реестре, если она нашла это в манифесте.

    Без регистрации. Без установки. Без админских прав. Уже четверть века этот способ доступен. И это не пасхалка, это документированный способ.

И для примера небольшой сниппет, касающийся упомянутого ранее Direct3D 9.

Вот такой сниппет:

Public Function TestThisStuff() As Long
    Dim D3D_RootObject As IDirect3D9
    
    Set D3D_RootObject = Direct3DCreate9(32)
    TestThisStuff = D3D_RootObject.GetAdapterCount()
End Function

Здесь просто создаётся корневой объект объектной модели D3D и через него мы дёргаем метод GetAdapterCount(), чтобы узнать число видеоапаптеров в системе. Можно было и какую-то примитивную 3D-игру написать типа бильярда, но смотреть на это в дизассемблере было бы не весело. Поэтому просто такой наипростейший пример.

На C++ то же самое выглядело бы вот так:

long TestThisStuff()
{
    IDirect3D9* D3D_RootObject;

    D3D_RootObject = Direct3DCreate9(D3D_SDK_VERSION /* 32 */);
    return D3D_RootObject->GetAdapterCount();
}
Я знаю, что этот код содержит баг

С C++-коде выше недостаёт вызова D3D_RootObject->Release() перед возвратом и возникает нарушение подсчёта ссылок/утечка памяти. По феншую указатель на IDirect3D9* в шаблонный враппер _com_ptr_t<>

Если скомпилировать этот VB-сниппет, то получится исполняемый файл, который просто тупо импортирует 1 функцию из D3D9.DLL:

Если посмотреть дизасм функции TestThisStuff, то получим следующее:

Не сильно-то это отличается от того, чтобы сгенерировалось для аналогичного C++-кода (только там бы автоматически не было установки хендлера исключений и копирования/освобождения ссылки на объект (за которым стоит IUnknown::AddRef и IUnknown::Release для правильного подсчёта ссылок).

Ну и что здесь происходит? Сначала просто вызывается функция из DLL, точно так же, как была бы вызвана, к примеру, функция CreateProcessA из kernel32.dll — в ответ функция возвращается ссылку на корневой объект объектной модели D3D. И сразу же у этого объекта дёргается метод GetAdapterCount, через vtbl, метод является 4-м по счёту у интерфейса IDirect3D9, поэтому его смещение в vtbl — 0x10. поэтому тут мы видим CALL [ECX+10]

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

Выводы:

  • Не хотите для своих библиотек использовать единый для всех унифицированный способ с помощью которого система сможет находить вашу DLL и делать все манипуляции, требуемые для создания нужного вам объекта — не делайте. VB сможет работать с такой библиотекой.

  • Либо можете использовать этот унифицированный способ (экспортировать из библиотеки DllGetClassObject, возвращающую указатель на фабрику класса), но если вам претит регистрация в реестре, можете в конкретно своём случае эту библиотеку не регистрировать в реестре, а вместо этого в своём приложении вручную и самостоятельно находить библиотеку, самостотельно загружать её, самостоятельно вызывать DllGetClassObject и самостоятельно вызывать CreateInstance у фабрики классов. Тогда в будущем, если вы перестанете бояться, вы сможете начать регистрировать эту библиотеку, ничего в ней не переделывая, но пока что вы можете пользоваться ею, избегая регистрации.

  • Либо, если вы приняли для себя факт существования Windows XP или более поздних систем, вы можете ни на уровне библиотеки не делать ничего экстраординарного, ни на уровне клиентского приложения тоже не делать ничего экстраординарного, но просто вместо регистрации в реестре положить рядом с приложением саму библиотеку и манифест-файл.

А теперь что касается двойного дна у комментария, про котрое я сказал в самом начале. Изначально я в предшествующем комментарии неаписал: что мешало наплодить миллион библиотек, как это сделали в сообществе Python? На что был ответ, что всё это ресурсо-ёмкое и вообще надо регистрировать в реестре. Вроде бы как мы выяснили, что ничего там ресурсоёмкого нет, а регистрация в реестре не единственный вариант, а имеется альтернатива. Но даже если бы всё было так, хочется спросить: а разве в Python поиск библиотека не более ресурсоёмкий? А разве в Python вызов метода класса у объекта-экземпляра — не более ресурсоёмкий? А разве в Python не нужно так же инсталлировать/регистрировать библиотеку вызовом какого-нибудь pip install?

Случайно умножили на единицу, и решили, что ничего страшного в этом нет. Но забыли, что единица — мнимая.

Что именно мешало написать мегатонны разноплановых библиотек для VB, таких же по широте возможностей, как наплодили для Python, в виде COM-библиотек? Написать из можно было хоть на самом VB (как пишут библиотеки для Python-а на самом Python-е), так и на практических любых других языках (например C++). И что самое главное, использовать их в таком случае можно было бы не только из VB, но из кучи других близкородственных сред (VBA, VBScript, JScript), нейтральных (C, C++) и не очень близкородственных (PHP) сред. Благодаря тому, что технологии COM/ActiveX предлагали многим какой-то один чётко зафиксированный языко-независимый формат/механизм/протокол взаимодействия.

что продукт борланда сильнее и мощнее тогда был - неоспорим,

Как раз таки оспорим.

VLC была более обширной, целая гора элементов управления прямо из коробки. Но вот компилятор у Борланда генерировал более убогий машинный код — проверено. Линкер борландовский генерировал раздутые исполняемые файлы, не умею сливать секции импорта линкуемых объектников в один большой дескриптор импорта (с одной объединённой IAT/ILT) для каждой отдельно взятой импортируемой библиотеки).

В Delphi не было возможность стопнуться на брекпоинте и кардинально перекроить код процедуры, в которой вы остановились, убрав какие-то строки, добавив какие-то строки, поменяв какие-то строки местами, а затем продолжить выполнение с любого места. А в VB — было.

Также и работа с COM-объектами была менее удобной, если говорить о чужих COM-объектах, а написание своего COM-сервера или ActiveX-сервера тоже была более тяжёлой задачей на Delphi.

У него конечно положительная плавучесть

У него же клапаны для уравнивания давления с атмосферным там, насколько я знаю.

А так как она не взаимодействует непосредственно с запуском, то и не обязательно выдерживать большие нагрузки ( кроме теплового щита) А железные фермы достаточно соблюсти геометрию. Выправить для придания формы, а дальше дело техники поднять, перевернуть и вставить обратно.

Помимо того, что тут что-то неладное с русским языком, хочется сказать: правка стержневых элементов подобных ферм — дело очень сложное и неблагодарное. Прогресс давно научился делать рихтование отдельных стержневых элементов (недавно видел ЧПУ-станок, который в идеал выводит погнутый во многих местах стальной вал). Но правка фермы — дело другое. Приложение усилий к одному элементу фермы будет не только править его, но и передавать усилие и деформировать смежные элементы (тем более, что они уже потеряли устойчивость из-за деформаций). Во-вторых, для правки такой фермы нужен соответствующих размеров стапель, обладающий многократным запасом по прочности и жёсткости, чтобы самому не деформироваться в процессе правки «жертвы».

В итоге, технически целесообразнее, рациональнее дешевле и проще вырезать из стального проката новые элементы и сварить новые фермы, нежели выгибать то, что есть. Тем более, что ЧПУ лазеры/плзамы для нарезки проката давно есть, и скорее всего у космических структур типа ЦЭНКИ они уже стоят в цехах.

Так, а вот например дело дойдет до вызова pure virtual метода? Куда улетит выполнение? Что там начнет при этом выполняться — на голом железе , возможно ещё даже без инициализированной виртуальной памяти? Библиотечный stub, который выводит сообщение силами линукса?

Не боитесь и не предвидите, что когда знаний станет сколько нужно, идея писать на C++ да ещё и со становлением в зависимость от стандартной библиотеки станет совсем не привлекательной?

И то и то является пользовательскими контролами для VB

Только первое является пользовательскими контролами именно для VB. Второе является пользовательскими контролами для чего угодно на свете (если границами «белого света» считать мир Windows).

С минимумом изменений? Они сделали 64-битную версию VBA, добавили тип данных LongLong и TDC для него. Это не минимум изменений, это очень сложная переботка, учитывая что виртуальная машина целиком и полностью была написана ассемблере и помимо неё в коде EB было очень много фрагментов, пришедших ещё с 16-битной эпохи.

Или претензия в чём? Вы не смотрите на 7.1, вы смотрите по билд-номерам библиотеки EB/VBA. Или по релизам офиса, в которых попадала VBA. Насколько таким образом VBA по дате своего последнего релиза пережила VB6 (98-й год)?

Кстати, и для VB6 последний релиз был вовсе не в 98-м году, потому что выпускались многочисленные SP к нему.

Информация

В рейтинге
Не участвует
Откуда
Петропавловск, Северо-Казахстанская обл., Казахстан
Зарегистрирован
Активность

Специализация

Десктоп разработчик, Инженер встраиваемых систем
Pure C
Assembler
X86 asm
Win32 API
Visual Basic
MySQL
Git
ООП
Разработка электроники
Обратная разработка