Native DLL с помощью чистого VB6 без примочек

Разговоры на любые темы: вы можете обсудить здесь какой-либо сайт, найти единомышленников или просто пообщаться...
Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 29.11.2011 (Вт) 21:51

arthur2 писал(а):Сколько же ещё всего в нашем бейсике запрятано, что никто не знает?

Там много интересного. Некоторые находки не столько революционны, сколько просто интересны.

Вот я всегда думал, что у проекта есть один параметр: стартовый объект. Это может быть либо Sub Main, либо какая-либо форма. Что не может быть одновременно и то, и это.
Изображение

На самом деле, это всего-лишь упрощение со стороны диалога свойств проекта. У проекта на самом деле два параметра:
  • Адрес Sub Main (возможен вариант, когда Sub Main отсутствует)
  • Список объектов, которые по терминологии рантайма называются Predeclared Objects. Это экземпляры классов (форм), которые должны быть автоматически порождены при старте (до вызова Sub Main!)

Так что если вам казалось, что может либо Sub Main, либо Form1, но не оба сразу — это заблуждение. Можно быть одновременно и Sub Main, и Form1.
То есть по идее вместо комбо-бокса Startup Object должно было быть два элемента в диалоге свойств проекта: элемент для выбора стартовой процедуры и список с чекбоксами для выбора predeclared-объектов.

И всё в таком духе. Теперь я знаю, где границы ответственности между VB6.EXE и VBA6.DLL. Чем отличается Lpproj от hproj. Module от hbasmod. То есть если в вот этой твоей теме мы просто хлопали глазами, то теперь я очень хорошо представляю, что это за функции, что они принимают и что возвращают. И что делают :)
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение arthur2 » 08.12.2011 (Чт) 11:06

Хакер писал(а):То есть если в вот этой твоей теме мы просто хлопали глазами, то теперь я очень хорошо представляю, что это за функции, что они принимают и что возвращают. И что делают :)
Так ты теперь знаешь, как получить ссылку на отлаживаемый проект?

А может попутно нашел, как-таки выловить End? Помнится, ты говорил, что на самом деле в переходнике под ИДЕ для AddresOff нужно что-то подправить - никак не могу сейчас найти ту тему.
Артур
 
   

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 08.12.2011 (Чт) 11:15

arthur2 писал(а):А может попутно нашел, как-таки выловить End?

End — это вызов вызов внутренней функции DbgReset. Он в конечном счёте приводит к вызову EbProjectReset (она экспортируется VBA).
Но не нужно полагать, что ресет тождественен End-у. «Reset» выполняется и при запуске проекта, и делает сброс контекста апартамента. Поэтому, в частности, если сделать в Immediate Window что-то вроле d = 123 до запуска проекта, а после запуска сделать там же MsgBox d, то мы не увидим 123. И аналогично, если установить переменную во время отладки, и проверить после стопа.

arthur2 писал(а):Помнится, ты говорил, что на самом деле в переходнике под ИДЕ для AddresOff нужно что-то подправить

Да, там в переходнике уже имеется аварийная заглушка на случай вызова функции по адресу после остановки проекта. Но в качестве числа параметров всегда подставляется 0, поэтому заглушка портит стек и приводит к краху.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение ger_kar » 08.12.2011 (Чт) 11:20

А мне вот интересно как можно в одном потоке сделать одновременный запуск и sub main и Form1 походу эта фишка актуальна будет только для многопоточности. Да и надобности в том, что-бы можно было одновременно выставить и то и другое я не нахожу. И возникает естественный вопрос, а зачем оно нужно?
Бороться и искать, найти и перепрятать

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 08.12.2011 (Чт) 11:25

ger_kar писал(а):А мне вот интересно как можно в одном потоке сделать одновременный запуск и sub main и Form1 походу эта фишка актуальна будет только для многопоточности.

Ей богу — странный вопрос. Есть два отдельных, скажем так, параметра проекта, просто диалог свойств проекта управляем обоими как одним. Причём тут поток? Не нужно же думать, что одновременный запуск это воистину одновременный параллельный запуск. Сначала порожается экземпляр формы, а затем уже происходит вызов SubMain-а. События Initialize, Load формы срабатывают до вызова Sub Main.

ger_kar писал(а):И возникает естественный вопрос, а зачем оно нужно?

К тому, что Sub Main в VB — это идеологически не тот main, что, например, в Си/Си++. Это что-то вроде обработчика события «проект для данного аппартамента нужно инициализировать». Достаточно посмотреть, как и сколько раз вызывается Sub Main в ActiveX-серверах с моделью Apartament Threaded.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

ger_kar
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1957
Зарегистрирован: 19.05.2011 (Чт) 19:23
Откуда: Кыргызстан, Иссык-Куль, г. Каракол

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение ger_kar » 08.12.2011 (Чт) 11:37

Хакер писал(а):Ей богу — странный вопрос.
Ну уж какой возник :) .
А теперь после ответа, все встало на свои места (в голове конечно). А не было бы вопроса, не было бы и ответа.
Бороться и искать, найти и перепрятать

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 04.01.2012 (Ср) 21:51

Сейчас больше часа парился - написал dll, компилируется, при вызове падает. Вроде бы нет ничего, что бы требовало эту самую "инициализацию рантайма". Потом подумал, что, возможно, ф-ция Sqr без этого не обходится, заменил в коде все Sqr на Sqrt (написал свою ф-цию, добился той же точности на синглах) - не помогло.
Оказалось, что рантайма требуют... статические переменные ф-ции! Я их просто перенёс в корень модуля, и всё заработало, даже Sqr, оказалось, я зря убирал, он работает.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 04.01.2012 (Ср) 22:58

/* При написании сообщения я забыл, что пишу не в эту тему. Так что непосвящённые должны учитывать, что описанное в этом сообщении относится к этой теме. */

Я решил эту проблему просто:
Код: Выделить всё
If Not bInitiealized Then
    Set InitializationHolder = CreateObject("ThisProject.TheOnlyClass")
    bInitialized = True
End If


Теперь, пока экземпляр жив, можно делать внутри RTICK практически всё (контекст инициализирован), кроме выкидывания исключений. А если саму RTICK сделать методом класса, и вызывать этот метод из экспортируемой RTICK — то можно вообще делать абсолютно всё. Что я и сделал.

Кстати, почему бы тебе было не сделать так, чтобы хост-программа требовала ActiveXDLL, а не dll с экспортом? Вероятно, что кол-во VB-участников было бы побольше.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 05.01.2012 (Чт) 8:35

То есть есть достаточно простой способ "локальной" инициализации рантайма? Отлично, буду разбираться.
Хакер писал(а):почему бы тебе было не сделать так, чтобы хост-программа требовала ActiveXDLL, а не dll с экспортом? Вероятно, что кол-во VB-участников было бы побольше.

Участников на всех остальных языках было бы меньше, а VB программисты что-то не очень активны. Правда можно было предоставить возможность выбора одного из вариантов, поздно сообразил.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 05.01.2012 (Чт) 11:24

Mikle писал(а):То есть есть достаточно простой способ "локальной" инициализации рантайма? Отлично, буду разбираться.

Ну этот способ требует того, чтобы библиотека была зарегистированной. Не всегда это хорошо, не всегда это возможно, поэтому правильный способ, не требующий регистрации — это вызывать DllGetClassObject прямо у самой себя, в обход [CreateObject→CoCreateInstance→Реестр]. Или VBDllGetClassObject прямо у рантайма. Но самый правильный: вызывать CThreadPool::InitDllThread — но это практически нереально для VB-программистов.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 05.01.2012 (Чт) 11:58

Вообще, механизм получается такой:
  • Мы вызываем CreateObject.
  • CreateObject вызывает CLSIDFromProgID, чтобы получить CLSID из ProgId-а — это первое обращение к реестру, требующее регистрации.
  • CreateObject вызывает CoCreateInstance.
  • CoCreateInstance вызывает CoGetClassObject.
  • CoGetClassObject ищет в реестре имя dll-файле на базе CLSID-а (ключ «InprocServer32») — это второе обращение к реестру, требующее регистрации.
  • CoGetClassObject вызывает у найденного dll-файла функцию DllGetClassObject.
  • DllGetClassObject в нашей DLL вызывает VBDllGetClassObject из рантайма.
  • VBDllGetClassObject в рантайме вызывает CThreadPool::InitDllAccess.
  • CThreadPool::InitDllAccess вызывает CVBThreadAction::InitDllThrd.

По-настоящему нужен нам только последний пункт списка (для иницализации контекста), но мы вольны начинать действовать с любого элемента в цепочке, выбирая кол-во лишних действий. Самый конечно простой вариант — это вызвать CreateObject, начав с начала цепочки.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 05.01.2012 (Чт) 13:57

Не понимаю, как связан рантайм со статическими локальными переменными. Разве это не то же самое, что обычные переменные, объявленные на уровне модуля, только с другой зоной видимости.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 07.01.2012 (Сб) 20:10

jangle писал(а):Native DLL созданные на VB можно вызывать из других сред разработки? Например PowerBASIC

Хакер писал(а):Да хоть из других, хоть с помощью rundll32 — можно.
Только DLL без инициализации рантайма написанные без учёта всех ограничений скорее всего упадут.

Похоже, что ни фига нельзя. Из vb.net не хочет работать, проверял твою dll из первого поста:
Код: Выделить всё
Public Class Form1
  Private Declare Sub Bar Lib "1" ()
  Private Declare Sub Foo Lib "1" ()

  Private Sub Form1_MouseDown(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
    If e.Button = Windows.Forms.MouseButtons.Left Then
      Foo()
    ElseIf e.Button = Windows.Forms.MouseButtons.Right Then
      Bar()
    End If
  End Sub
End Class

Выдаёт сообщение:
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

vb.net выбрал, как наиболее для меня простой, после VB6, инструмент.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 07.01.2012 (Сб) 21:04

Похоже, что ни фига нельзя. Из vb.net не хочет работать, проверял твою dll из первого поста:
...
Attempted to read or write protected memory. This is often an indication that other memory is corrupt.
vb.net выбрал, как наиболее для меня простой, после VB6, инструмент.



Хакер писал(а):Да хоть из других, хоть с помощью rundll32 — можно.
Только DLL без инициализации рантайма написанные без учёта всех ограничений скорее всего упадут.


Если перечитать выделенные фрагменты, тебе не кажется, что ты несколько неправ?
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 07.01.2012 (Сб) 21:33

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

Какие ограничения не учтены в той DLL? Можно привести пример DLL без инициализации рантайма но c учётом всех ограничений?

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 07.01.2012 (Сб) 21:40

Mikle писал(а):Какие ограничения не учтены в той DLL? Можно привести пример DLL без инициализации рантайма но c учётом всех
Можно. Что должен делать пример?

З.Ы.
Вот код, написанный на VB, который внедрялся в чужой процесс. Там не то, что контекст был не инициализирован, там даже самого рантайма не было. И ничего :)
Код: Выделить всё
Private Function BEGIN_OF_INJECTION_CODE() As Long ' Не менять!
BEGIN_OF_INJECTION_CODE = BEGIN_OF_INJECTION_CODE_MARKER: End Function

'=======================================================================

'
' Реализация всех внедряемых функций должна быть расположена только
' здесь: между BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE. Это
' не относится к функции-обработчику прерывания, так как он никак
' не связан с функциями поддержки.
'
' На функции накладываются следующие ограничения:
'  1) Все внутренние функции, вызываемые из этих, должны быть так же
'     объявлены в это регионе (между BEGIN... и END_OF_INJECTION_CODE).
'  2) Все внешние функции, вызываемые из этих, должны быть объявлены
'     в TLB.
'  3) Код должен быть написан так, чтобы не использовался рантайм VB.
'     Это проще, чем кажется, надо просто писать так, чтобы он был
'     не нужен. Все функции написаны таким образом, будьте бдительны,
'     если решите вносить в них какие-то свои изменения.
'

Private Sub InterruptPreparation(ByRef id As HANDMADE_INTERRUPT_DATA)
    '
    ' Эта функция вызывается ДО вызова обработчика прерывания в кон-
    ' тексте процесса и потока, в котором инициируется прерывание.
    ' Всё, что должна сделать эта функция: сохранить значение потоко-
    ' специфичной переменной LastErrorValue (хранится в TEB), чтобы
    ' восстановить после завершения работы обработчика прерывания.
    '
   
    id.SoftwareContext.SavedLastError = GetLastError()
End Sub



Private Sub InterruptPostProcessing(ByRef id As HANDMADE_INTERRUPT_DATA)
    '
    ' Эта функция вызывается ПОСЛЕ вызова обработчика прерывания в кон-
    ' тексте процесса и потока, в котором инициируется прерывание.
    ' Она должна вернуть старое значение переменой LastErrorValue, про-
    ' верить работоспособность хост-процесс, если он жив — установить
    ' объект событие и ждать, пока хост-процесс исправить контекст
    ' потока. Если хост-процесс мёртв, эта функция попробует самостоя-
    ' тельно исправить свой поток через выброс исключения и его обработ-
    ' ку.
    '
   
   
    '
    ' Проверяем, жив ли процесс (вернее поток, который всё вернёт
    ' на свои места). Если нет, имеет смысл решить проблему своими сила-
    ' ми, попутно закрыв хендлы. Если жив, он сам закроет эти хендлы
    ' в конце процесса восстановления состояния прерванного потока.
    '
       
    Select Case WaitForSingleObject(id.hHostThread, 0)
        Case WAIT_TIMEOUT
            '
            ' Поток работает. Возвращаем старое значение LastErroValue и
            ' сигнализируем ему (потоку), что обработчик прерывания за-
            ' вершил свою работу.
            '
           
            SetLastError id.SoftwareContext.SavedLastError
           
            If SetEvent(id.hCompletionEvent) Then
                Do
                    '
                    ' Беспечно циклически отдаём квант процессорного
                    ' времени другим потокам, в ожидании того, что
                    ' этот поток будет заморожен (SuspendThread) и
                    ' восстанволен в исходное состояние, а затем
                    ' разморожен (ResumeThread).
                    '
                   
                    SwitchToThread
                   
                    If WaitForSingleObject(id.hHostThread, _
                                           0) <> WAIT_TIMEOUT Then
                        '
                        ' Пока мы ждали, что хост-процесс нас починит,
                        ' хост-поток умер, так что никто (кроме нас
                        ' самих) нас уже не починит. Выходим их цикла
                        ' ожидания.
                        '
                       
                        id.hHostThread = 0&
                        Exit Do
                    End If
                Loop
            End If
           
            '
            ' В норме выполнение не должно дойти до сюда. Но если это
            ' произошло, значит либо хост-поток умер уже после выста-
            ' вления события, либо само событие выставить не удалось.
            '
           
            GoTo AbnormalSelfRecovery ' а как иначе, если сишный break
                                      ' «ставится» по умолчанию? ;(
        Case WAIT_OBJECT_0
            '
            ' Поток мёртв (завершил свою работу), поэтому закрываем
            ' хендлы и зануляем их в структуре, чтобы обработчик
            ' прерывания понял, что прерывания вызвано по случаю
            ' плачевного состояния хост-процесса/хост-потока.
            '
           
AbnormalSelfRecovery:
            If id.hHostThread <> 0& Then
                CloseHandle id.hHostThread
                id.hHostThread = 0&
            End If
           
            CloseHandle id.hHostProcess
            id.hHostProcess = 0&
           
           
            '
            ' Всю остальную работу выполнит обработчик (наш же) исключе-
            ' ния. Инициируем исключение.
            '
           
            ' RaiseException FIX IT!
       
           
        Case WAIT_FAILED
            '
            ' Получить состояние объекта-потока не получилось. Интересно,
            ' что надо делать в этом случае?..
           
    End Select
   
   
   
End Sub


'=======================================================================

Private Function END_OF_INJECTION_CODE() As Long ' Не менять!
END_OF_INJECTION_CODE = END_OF_INJECTION_CODE_MARKER: End Function
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 07.01.2012 (Сб) 22:03

Спасибо, добавил ф-ции BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE, от MsgBox избавился, теперь работает.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 07.01.2012 (Сб) 23:07

Не понял. Причём тут BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE??? Это просто маркеры в моей программе, которые используются для того, чтобы найти кусок, который нужно копировать в чужое АП.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 08.01.2012 (Вс) 8:47

Хакер писал(а):Не понял.

Я тоже не понял. Я попросил пример, ты дал, я почитал в нём коментарии, принял их за руководство к действию, в частности:
Код: Выделить всё
' Реализация всех внедряемых функций должна быть расположена только
' здесь: между BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE.

сделал, теперь работает...
Так изначально виноват был MsgBox? Просто ты не ответил на вопрос:
Mikle писал(а):Какие ограничения не учтены в той DLL?

Хорошо бы иметь список этих ограничений.

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 08.01.2012 (Вс) 19:48

Mikle писал(а):Я тоже не понял. Я попросил пример, ты дал, я почитал в нём коментарии, принял их за руководство к действию, в частности:
Код: Выделить всё
' Реализация всех внедряемых функций должна быть расположена только
' здесь: между BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE.

сделал, теперь работает...

То есть ты поверил, что есть особые имена для функций, которые VB особенным образом воспринимает, и об этом никто не знает, и я об этом нигде не написал? И причём тут вообще Injection, если у тебя нет никакого внедрения.

В общем, дело-то в написании самих функций, а не в магии.

Mikle писал(а):Хорошо бы иметь список этих ограничений.

Сложно сказать. Я когда вижу любой код, практически ясно представляю, во что он скомпилируется. И нужно просто писать его так, чтобы он скомпилровался таким образом, чтобы там была только арифметика (в том числе адресная), ветвления, и вызова WinAPI-функций. Чтобы обращений к рантайму не было. Не в плане отсутствия вызова рантаймовых функций, а в плане отсутствия использования таких обращений, которые требуют живого контекста.

Если нет такого чувства, как у меня, то нужно его выработать: просто смотришь, во что компилируется твой код, модифицируешь при случае. Такие вещи, как например, сложение двух чисел не требуют рантайма, но только если не произойдёт переполнение. Это смешно, и это очень примитивная задача, но если говорить о механических операциях, то возможно написать, например, сортировку массива, не требующую рантайма. А если вопрос во взаимодействии с системой: то тоже — всё что угодно, используя WinAPI объявленные в TLB, и типы, не требующие вмешательства рантайма.

Но вообще, чем мучаться и пытаться уложиться в трудно-формулируемые ограничения, проще ведь «поднять» контекст, не так ли? Я описал «грязный метод», я могу как-нибудь написать хороший bootstrap-модуль, и в конце-концов, AMC-Tools решит эту задачу.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

VBTerminator
Постоялец
Постоялец
Аватара пользователя
 
Сообщения: 415
Зарегистрирован: 19.11.2008 (Ср) 20:10

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение VBTerminator » 08.01.2012 (Вс) 20:33

Хакер писал(а):Чтобы обращений к рантайму не было. Не в плане отсутствия вызова рантаймовых функций, а в плане отсутствия использования таких обращений, которые требуют живого контекста.

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

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 08.01.2012 (Вс) 21:39

Хакер писал(а):То есть ты поверил, что есть особые имена для функций, которые VB особенным образом воспринимает

Не VB особенным образом воспринимает, а какие-то маркеры внутри COM DLL, я не знаю этого формата (COM), и особого смысла изучать уже не вижу, даже DLL использую, как нативную, а COM она получилась "в нагрузку", раз VB по другому не умеет.
Хакер писал(а):Сложно сказать. Я когда вижу любой код, практически ясно представляю, во что он скомпилируется.

Я считал, что тоже, я ожидал, что MsgBox скомпилируется в вызов API ф-ции. Хотя да, он работает со строками, а строковые ф-ции, вроде как, работают через рантайм, мог бы догадаться. Но Static переменные тут при чём? Я не представляю, как надо скомпилировать, чтобы они использовали рантайм, да ещё и требовали инициализацию.
Хакер писал(а):чем мучаться и пытаться уложиться в трудно-формулируемые ограничения, проще ведь «поднять» контекст, не так ли?

Пока не знаю, на сколько проще. Если это сведётся к подключению модуля с парой ф-ций, одну из которых вызываем про первом обращении к DLL, вторую при последнем (к примеру), и если этот модуль напишешь ты... :roll:

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 08.01.2012 (Вс) 21:49

Mikle писал(а):я не знаю этого формата (COM), и особого смысла изучать уже не вижу, даже DLL использую, как нативную, а COM она получилась "в нагрузку", раз VB по другому не умеет.

:shock: :shock: :shock:
Нет никакого формата, это обычная DLL.

Mikle писал(а):Я считал, что тоже, я ожидал, что MsgBox скомпилируется в вызов API ф-ции.

Странные у тебя представления. MsgBox — это обычная функция, объявленная в TLB-шке, подключенной проекту. Если бы там была объявлена функци Bizpieqihiug из библиотеки soh98w.dll, то вызов Bizpieqihiug скомпилировался бы в вызов Bizpieqihiug, и ни что иное. Но мы имеем в TLB-шке фунцию MsgBox с внутреннем именем rtcMsgBox. И вызов rtcMsgBox компилируется в вызов rtcMsgBox. VB никаким особым образом не обрабатывает вызов MsgBox по сравнению с остальными функциями рантайма, и VB никаким особым образом не обарабтывает вызовы функций рантайма по сравнению с вызовами не рантайма. Все вызовы всех функций — на общих основаниях. В этом соль, это надо прочувствовать.

Mikle писал(а):Хотя да, он работает со строками, а строковые ф-ции, вроде как, работают через рантайм, мог бы догадаться.

Дело совершенно не в том, что строковые функцию работают через райнтайм. Просто MsgBox это не тупая обёртка над MessageBox. MessageBox от системы — это функция, показывающая модальное диалоговое окно. А MsgBox от VB — это функция, как один из вариантов, вызывающая MessageBox. А как другой из вариантов — помещающая сообщение в системный журнал событий (или в свойствах проекта поставлен unattended-флаг).

Mikle писал(а):Но Static переменные тут при чём? Я не представляю, как надо скомпилировать, чтобы они использовали рантайм, да ещё и требовали инициализацию.

Ты так говоришь, как будто ты не открыл первым же делом полученный dll файл с помощью любого дизассемблера и не посмотрел, что там не так... Стоп, а может быть ты действительно не открыл и не посмотрел? :?
Доступ к статическим переменным осуществляется как доступ к блоку, на который указывает указатель. Если контекст не инициализирован, указатель занулён, что и вызывает падение.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Mikle
Изобретатель велосипедов
Изобретатель велосипедов
Аватара пользователя
 
Сообщения: 4148
Зарегистрирован: 25.03.2003 (Вт) 14:02
Откуда: Туапсе

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Mikle » 09.01.2012 (Пн) 9:21

Хакер писал(а):Нет никакого формата, это обычная DLL.

Ну да, это не просто COM, это ActiveX. Я понимаю, что это - тоже обычная DLL, но, всё же, со своими соглашениями.
Раз это - обычная DLL, значит там есть DllMain, которая обрабатывает случаи DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH. Но я не писал DllMain! Значит VB сгенерировал её сам. Так почему бы мне не предположить, что для ActiveX он сгенерирует какие-то BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE? Думаю, моя логика понятна?
Хакер писал(а):Просто MsgBox это не тупая обёртка над MessageBox

Я именно это предполагал.
Хакер писал(а):Ты так говоришь, как будто ты не открыл первым же делом полученный dll файл с помощью любого дизассемблера и не посмотрел, что там не так... Стоп, а может быть ты действительно не открыл и не посмотрел?

Ты так говоришь, как будто открыть vb6 EXE в дизассемблере и найти, во что там скомпилировались статичные переменные, это дело пяти минут. Может для тебя так и есть, если ты уже не первый год разглядываешь эти EXE под микроскопом. Я это делал всего пару раз для того, чтобы посмотреть, как код оптимизируется, и то, чтобы было легче искать, вставлял в код рядом метки, например "x = x + &H12345678", потом ищу константу "12345678h".
Хакер писал(а):Доступ к статическим переменным осуществляется как доступ к блоку, на который указывает указатель

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

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 09.01.2012 (Пн) 9:48

Mikle писал(а):Раз это - обычная DLL, значит там есть DllMain, которая обрабатывает случаи DLL_PROCESS_ATTACH, DLL_THREAD_ATTACH, DLL_THREAD_DETACH, DLL_PROCESS_DETACH. Но я не писал DllMain! Значит VB сгенерировал её сам.

Не совсем сгенерировал. Она уже предварительно подготовленная. И является переходником к рантаймовой DllMain. Но вообще у обычной DLL может и не быть DllMain вовсе.

Mikle писал(а):Так почему бы мне не предположить, что для ActiveX он сгенерирует какие-то BEGIN_OF_INJECTION_CODE и END_OF_INJECTION_CODE?

Потому что DllMain есть в мире, а BEGIN_OF_INJECTION_CODE и её напарника — нет. Видно же из примера, что это нечто, относящееся к задаче внедрени (и нужно, чтобы найти кусок, который будет скопирован. И что демонстрировось не решение проблемы, а код, не использующий рантайм воовсе.

Mikle писал(а):ы так говоришь, как будто открыть vb6 EXE в дизассемблере и найти, во что там скомпилировались статичные переменные, это дело пяти минут.

Это дело 1 секуны. У тебя же экспортируемые функции. Допустим экспортируется функция Foo. Ты открываешь OllyDbg и пишешь в командной строке: at Foo. И сразу видишь код функции. Не надо ничего искать даже.

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

Нет. Там сделано так: все глобальные переменные — это глобальные переменные в классическом смысле, а для всех статических переменных сделана одна глобальная-переменная указатель, указывающая на блок статических переменных. Проверки нет, указатель инициализируется при поднятии контекста.

Зачем они так сделали (для меня это, надо сказать, сюрприз) я пока не готов ответить. Наверняка смысл есть.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение arthur2 » 20.02.2014 (Чт) 12:01

arthur2 писал(а):И, кстати, не такие уж и большие ограничения при неподнятом рантайме :) Недоступно только то, что использует классы (самая большая потеря - обработка ошибок), ну и апи нужно объявлять через тлб, декларация не прокатит. Работа со строками, массивы, стандартные функции и прочее - всё работает (поскольку не требует поднятия рантайма, а просто из него экспортируется).

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

Код: Выделить всё
Option Explicit
Public Declare Function GetTickCount Lib "kernel32" () As Long
'#EXPORT
Function exportTest() As Long


Dim m As New Collection, i As Long

m.Add 5, "kk"
i = m.Item("kk")
MsgBox "Создание встроенных классов работает   " & i

  Dim myScript As MSScriptControl.ScriptControl
  Set myScript = New MSScriptControl.ScriptControl
   
  myScript.Language = "vbscript"
  myScript.ExecuteStatement "msgbox " & """" & myScript.Eval("(2+5)* 6") & _
"   Создание объектов из подключенных длл работает" & """"

On Error Resume Next
Dim k As Byte
k = 300
MsgBox "Обработка ошибок работает" & vbCrLf & Err.Description & "  " & Err.Number

MsgBox "Вызов АПИ через Declare работает" & vbCrLf & GetTickCount


Dim c As testDll.cTest 'самописная длл на бейсике
Set c = New testDll.cTest
If c.test Then MsgBox "создание объекта из самописной длл на бейсике тоже работает"

exportTest = 5

End Function


По сути, практически все встроенные средства работают. Нельзя только использовать СВОИ классы и формы. Интересно, почему? И таки да - не работают статик-переменные. Получается, если из другого языка - рантайм вообще не поднимается и можно использовать только импортируемые функции, а если из бейсика - то рантайм "приподнимается"? Но не до конца?
Артур
 
   

The trick
Постоялец
Постоялец
 
Сообщения: 781
Зарегистрирован: 26.06.2010 (Сб) 23:08

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение The trick » 26.02.2014 (Ср) 7:38

arthur2, я так полагаю что это происходит из-за неициализированного контекста потока (в терминологии Хакера), т.к. статичные переменные хранятся в TLS. Из-за этого и не работает многопоточность (она работает только в ограниченном виде) в StandartEXE.
UA6527P

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 26.02.2014 (Ср) 9:20

Кривоус Анатолий писал(а):из-за неициализированного контекста потока (в терминологии Хакера)

Не контекст потока, а просто контекст. Или контекст потока-проекта. Или контекст проекта?

Давайте ещё раз. Пирамида потребностей (потребности растут снизу вверх):
Изображение

Что касается райнтайма:
  • Есть сущности, которые являются общими для всех потоков, сколько бы их (потоков) не было. Инициализируются глобально для всех потоков. Например это касается вызова TlsAlloc, регистрации оконных классов для контролов и т.д.
  • Есть per-thread структуру данных, которые главным образом нужны для поддержки того, что на картинке размещено выше.

Если проекты расположить по горизонтали, а потоки — по вертикали, то получается матрица. Ячейку такой матрицы я называю контекстом. Код проекта P может работать в рамках потока T только если инициализирован контекст P×T.

Ни в коем случае не нужно путать контексты, и апартаменты. Контекст — это ячейка, а апартамент распространяется на всю колонку.

Кстати, помимо прочего, COM-апартамент тоже должен быть инициализирован (CoInitializeEx).

Под контекстом не нужно понимать ничего конкретного (вроде конкретной структуры или таблицы в памяти). Под контекстом подразумевается целая совокупность вещей:
  • Набор данных, которые рантайм поддерживает для каждого контекста.
  • Все глобальные и статические переменные проекта (хранилище под них).
  • Аппглобальный объект — COM-объект, реализуемый рантаймом, у которого есть такие свойства как App, Forms, Printers, Printer, Screen, Clipboard. App — это не глобальная переменная, это свойство COM-объекта, и каждого потока такой объект будет свой.


Тут важное замечание: у проектов с ThreadingModel=Single контекст может быть только один. Хранилищем глобальных переменных (включая неявную невидимую переменную-ссылку на аппглобальный объект) будет секция данных PE-модуля. Если ThreadingModel=Apartament, то контекстом может быть много, при этом хранилищем глобальных переменных для одного из контекстов (того, который был инициализирован первым) служит секция данных, для остальных контекстов в памяти выделяется альтернативное хранилище.

Так, если говорить о каком-то проекте, каждый поток будет обладать собственной копией всех глобальных переменных. И коллекция экземпляров форм тоже будет своя. Так работает VB-шный мезанизм TLS. Таким образом будет достигнута межпоточная изоляция.

Синяя клетка олицетворяет неинициализированный контекст, несуществующий, то есть такой, который только потенциально может существовать в данном месте, жёлтая — инициализированный. Для ActiveX DLL и OCX управление временем жизни контекстов подвязано ко времени жизни объектов. Даже если DLL/OCX загружена в АП процесса, контекст будет инициализирован только тогда, когда снаружи придёт запрос на порождение экземпляра класса (через DllGetClassObject). В проекте типа Standard EXE контекст инициализируется при запуске проекта (через ThunRTMain), а уничтожается при завершении программы. В проекте типа ActiveX EXE новый потоки порождаются самим проектом и контексты для них инициализируются автоматически.



_________________________

Все креши проектов как в случае попыток сделать NativeDLL, так и в случае попыток сделать многопоточность, можно расписать по следующей схеме:
  • Выполнение уходит в рантайм, где происходит вызов TlsGetValue
  • Если рантайм не подвергся глобальной инициализации, то TLS-слот для рантайма не выделен. Аргументом для TlsGetValue будет передано некорректное значение (ноль), функция сбойнет, возврат тоже будет некорректным. Это соответствует отсутствию инициализации нижнего большого розового блока.

  • Если рантайм не подвергся инициализации потоко-специфичных данных, то TlsGetValue вернёт бред, потому что предварительно не была вызвана TlsSetValue. Это соответствует отсутствию инициалиации маленького нижнего желтого блочка.

  • Если контекст не был инициализирован: рантайм для каждого потока содержит двухсвязный список проектов, которые работают в рамках текущего потока. Такой список для текущего потока будет получен, но в нём не будет найден элемент, соответствующий запрашиваемому проекту.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

arthur2
Продвинутый гуру
Продвинутый гуру
Аватара пользователя
 
Сообщения: 1688
Зарегистрирован: 23.01.2008 (Ср) 14:35

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение arthur2 » 27.02.2014 (Чт) 5:42

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

Хакер
Телепат
Телепат
Аватара пользователя
 
Сообщения: 16478
Зарегистрирован: 13.11.2005 (Вс) 2:43
Откуда: Казахстан, Петропавловск

Re: Native DLL с помощью чистого VB6 без примочек

Сообщение Хакер » 27.02.2014 (Чт) 5:45

Не понял.
—We separate their smiling faces from the rest of their body, Captain.
—That's right! We decapitate them.

Пред.След.

Вернуться в Народный треп

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 10

    TopList