|
-
Feb 14th, 2021, 12:49 PM
#41
Re: Instantiate internal class object with name in string
 Originally Posted by firehacker
Yesterday I enabled registration on VBStreets (without email confirmation)! Only 1 real human have registered so far, and zero(!) spammers. Apparently spammers excluded our forums from their lists. But they can strike back :-)
So, hurry up! Registration on VBStreets is now opened!
http://bbs.vbstreets.ru/ucp.php?mode=register
I can disable it at any moment when spammers get clue that registration is open again. And it will remain disabled until I fix emailing subsystem and apply some modern antispam solution. If you going to register, please also take care to remember your password, since password recovery won't work (except for manual recovery by PMing or emailing me)!
i'm chinese,I just registered as a member of this website. Let us learn together. Thank you for maintaining the forum.
your website htm is error for language :
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en-gb" xml:lang="en-gb">
<meta http-equiv="content-language" content="en-gb" />
you need change to :
<html dir="ltr" lang="ru-RU">
<meta name="author" content="Government.ru">
when i open registered url,it's not english,need a image button like "english version"
and change sumbit button
(now is :Reset Submit,need submit ,reset)
===================
I was infected with the LOCKBIT virus in April 2020. The VB6 test code and some project files I wrote before were encrypted. Knowing that the virus has whitelisted Russia, Ukraine and other countries, China has been ignored. I really hate viruses and hackers. I wonder if there is any way to help me retrieve the encrypted files. Time has passed so long, the link domain name of the ransomware virus cannot be opened. Is there a way?
Last edited by xiaoyao; Feb 14th, 2021 at 01:16 PM.
-
Apr 22nd, 2021, 06:39 AM
#42
Re: Instantiate internal class object with name in string
Hi firehacker, The trick and other vb experts,
I'd like to know if it is possible to disguise a module as a class/object to use. Because I want to use CallByName in the module, for example:
CallByName(Module1, sFuncName, VbMethod). Thanks!
-
Apr 22nd, 2021, 07:01 AM
#43
Re: Instantiate internal class object with name in string
AFAIK procedure names in bas modules don't get compiled. So you can't call them by name.
-
Apr 22nd, 2021, 07:04 AM
#44
Re: Instantiate internal class object with name in string
-
Apr 22nd, 2021, 09:45 AM
#45
Re: Instantiate internal class object with name in string
 Originally Posted by Eduardo-
AFAIK procedure names in bas modules don't get compiled. So you can't call them by name.
Hi Eduardo, thank you for the information.
Since firehacker can instantiate a class by name string, then he should have a way to use CallByName in vb-modules.
-
Apr 22nd, 2021, 09:47 AM
#46
Re: Instantiate internal class object with name in string
 Originally Posted by The trick
Only in IDE.
Hi The trick, do you mean that it is impossible to use CallByName in a module in IDE?
-
Apr 22nd, 2021, 10:25 AM
#47
Re: Instantiate internal class object with name in string
 Originally Posted by SearchingDataOnly
Hi The trick, do you mean that it is impossible to use CallByName in a module in IDE?
It is impossible compiled. Because the module's procedure names are not saved.
-
Apr 23rd, 2021, 11:57 AM
#48
Re: Instantiate internal class object with name in string
You can reference controls by name. For example, a textbox named Text1 can be referenced in code by name like this:
FormName("Text1").Text = "Hello, world!"
Not sure if that helps or not.
-
Apr 25th, 2021, 10:57 AM
#49
Re: Instantiate internal class object with name in string
 Originally Posted by Elroy
If you "think through" the code below, it becomes clear that the secret to instantiating an object with a string in an executable is all wrapped up in the "__vbaNew" call found in the msvbvm60.dll.
And Voila, reflection for classes fully implemented in VB6.
Should I post this in the CodeBank?
Enjoy,
Elroy
EDIT1: It turns out the lpObjectInfo I had wouldn't work because it changes value when things are moved into memory. Therefore, all of these procedure listed above are needed to instantiate an object from one of your internal classes from a string. Also, just FYI, once instantiated, TypeName works as expected.
IdeCreateInstance2 like IdeCreateInstance?
[CODE
]Dim TempObj As IUnknown
Private Function IdeCreateInstance2(ByVal Class As String) As IUnknown
' Only for IDE. '
EbExecuteLine StrPtr("NameBasedObjectFactory.OneCellQueue2 New " & Class), 0, 0, 0
'
Set IdeCreateInstance2 = TempObj
Set TempObj = Nothing
If IdeCreateInstance2 Is Nothing Then
Err.Raise 8, , "Specified class '" + Class + "' is not defined."
Exit Function
End If
End Function
Private Sub OneCellQueue2(ByVal refIn As IUnknown)
Set TempObj = refIn
End Sub[/CODE]
-
May 14th, 2021, 11:02 PM
#50
New Member
Re: Instantiate internal class object with name in string
 Originally Posted by SearchingDataOnly
if it is possible to disguise a module as a class/object to use. Because I want to use CallByName in the module, for example:
It is possible with some notices/preconditions.
Generally you can implement an equivalent of CallByName for plain procedures if you have a TLB describing these precedures. I should mention again, that TLB can not only describe COM interfaces and their methods, COM classes (co-classes) and their implemented interfaces, dispinterfaces, enums and constants, but plain modules and their members (procedures) can be described by TLB as well. Having combination of information about number and type of arguments, return type and procedure calling convertion is enough to implement a mechanism that can construct appropriate stack frame from original VARIANTs, pass executing to a procedure being called and clean-up stack afterwards.
Actually, we already have such mechanism within MSVBVM60.DLL as part of a so-called expression server. But it's major disadvantage that I discovered during reverse-engineering is that it limits itself to use only the TLB that is built into MSVBVM60.DLL itself. As a result, you can only call functions for MSVBVM: MsgBox, Beep, Abs, DoEvents, InputBox and any of others. You cannot call procedures from arbitrary executable modules (this limitation seems a bit artificial).
So you have to either implement such mechanism yourself (not so hard), or hack an existing expression server facility by applyting patches (and taking care to respect all the existing versions of MSVBVM60 and their differences).
For practical use, if you are going to call procedures not from some other DLLs (equipped with a TLB describing all public proecdures), but from your own project, in addition to implementing such mechanism you need to do two extra things:
- Make a TLB describing all your modules and their public procedures. You can use MIDL and compile TLBs manually, or you can make an Add-in that will construct TLBs using special CreateTypeLIb + ICreateTypeLib.
- Force VB to put these procedures into export table. Again, you can do it manually using those undocumented switches that I discovered ([VBCompiler] and LinkSwitches) or do it as part of your add-in.
Like it was done in my CreateInstanceByClassName, you'll likely need to make special handling for the case when your project is trying to call a procedure from the same project (or other project from the current project group) rather than from compiled executable.
-
May 16th, 2021, 10:15 AM
#51
Re: Instantiate internal class object with name in string
Very valuable knowledge, thank you very much, firehacker.
-
May 16th, 2021, 10:19 AM
#52
Re: Instantiate internal class object with name in string
 Originally Posted by SearchingDataOnly
Very valuable knowledge, thank you very much, firehacker.
Valuable but not actionable IMO.
If you are going to produce a type library with so much intricate info at run-time then you might as well just fill a Collection with keys on procedure names and values a pfn's (AddressOf MyProc) so you can just use DispInvoke API to emulate CallByName on a module.
cheers,
</wqw>
-
May 16th, 2021, 10:22 AM
#53
Re: Instantiate internal class object with name in string
 Originally Posted by wqweto
Valuable but not actionable IMO.
If you are going to produce a type library with so much intricate info at run-time then you might as well just fill a Collection with keys on procedure names and values a pfn's (AddressOf MyProc) so you can just use DispInvoke API to emulate CallByName on a module.
cheers,
</wqw>
Ok, I'll try your method. Thank you, wqweto.
-
May 16th, 2021, 09:20 PM
#54
Re: Instantiate internal class object with name in string
How to use code to dynamically add a new class or add several interfaces to the class? Or replace the original class, the OCX interface, with a function address in a module, or replace it with an interface in the Class1 class?
-
Jan 13th, 2022, 07:34 PM
#55
Addicted Member
Re: Instantiate internal class object with name in string
 Originally Posted by firehacker
Thank you for your wellcome.
During last ten years I spend thousands of hours reverse-engineering classic VB. I have so much unique information about its internals revealed during reverse-engineering...
I am in the middle of starting a new crowd-sourced project: it will be very similar to what ReactOS people are trying to do with Windows NT. It's not just a number of ephemeral wishes of reviving or recreating classic VB. There are some strong ideology, a methodology of work, specially designed toolkit and a toolchain behind my initiative.
I know there are some initiatives to re-make classic VB: people are starting from scratch and making their own IDEs and compilers utilizing completely different architectures and principles. For me such initiatives look like someone's attempt to create completely new operating system: you'll have no software for your new OS, no drivers, no community. The only reason why ReactOS has a chance to have any decent success is that they claim that software and drivers made for Windows NT will work (or have to work) under ReactOS and vise versa. They claim some degree of binary compatibility.
Likewise, I am targeted at recreating source files of original classic VB. Having such sources-pack will open a way of making improvements and releasing new versions of classic VB, and it will be higly compatible. All the existing VBP-projects will be compatible with hypothetical new IDE. All the existing binaries will be binary compatible with new recompiled msvbvm60.dll (with a number of improvements).
When it comes to reverse-engineering and ensuring high degree of compatibility, one would ask you: how can you surely tell that you reverse-engineered some product correctly and compiling your code (results of reverse-engineering) won't break compatibility? How can you guarantee that your half of a million lines of source code will behave exactly the same way as original product is supposed to work? What if you misunderstood some algorithm and your implementation is seems to be similar, but is not strictly identical?
The key aspect of my initiative is a special methodology that ensures that reverse-engineered source code is 100% equivalent to
the original source code (which Microsoft will never give us). I am talking about functional equivalence, I don't mean having exactly same name for procedures and variables (however, presence of debug symbols for VB6.EXE and MSVBVM60.DLL allows to achieve partial variable/functions/methods/classes/types match too!).
It isn't just a methodology: I have developed some unique tools that are used as part of the workflow by this methodology. The key utility is called ANTILEGO, but that requires an additional article on what the LEGO and LEGOfication process is (in a few words: it is a secret build stage that was used inside Microsoft when building all their binaries, it was a predecessor of PGO/LTCG in modern Microsoft's linkers, but it isn't the same as PGO/LTCG).
You probably heard of TDD as a development methodology. TDD stands for Test-Driven Development. In my case an important part of the declared methodology can be described as TDRE (Test-Driven Reverse Engineering). And, as I said, it's not just an ideology of applying some tests after each commit: I have a number of special utilities which form a toolchain which is a workhorse of the whole TDRE approach. These includes custom disassembling and sophisticated machine code analysis engines which can tell you if the machine code compiled from your recreated source file is functionally or even byte-to-byte equivalent of the original binary, released by Microsoft.
It is planned that the number of enthusiasts will work in a distributed manner (using Git), reversing the product part-by-part and proposing their results via pull-request mechanism. A special web site is going to be a coordination platform, showing the overall progress, a map of RCE coverage, having tons of documentation and serving as a task-tracker/bug-tracker as well as a hosting for Git repository.
The repository itself exists for a long period of time, but it isn't hosted anywhere in public repository hosting currently:
First commits were done 6 years ago (but the whole work started years before than, I just didn't use git):
Statistics and progress are displayed by local utilities and scripts:

(This is an old picture, BTW, but it shows how I am planning to display coverage map on the projects main page in the future)
Lots of functions from MSVBVM60.DLL, VB6.EXE and VBA6.DLL are reversed back to C/C++ code. Here is example of VBACollection::Remove (which is implementation of Remove method of Collection class in VB/VBA):

It is not just reversed, it is reversed the way that it's not simply functional equivalent of original code, but if you try to compile it with proper version of CL.EXE (Microsoft C/C++ compiler) with a proper set of command like switches, it gives a sequence of machine commands byte-to-byte identical to the sequence command that you can find in the binary released by Microsoft. And lots of functions and members are reverse-engineering this way, and my goal is to cover everything (but that is not a mandatory requirement if you're going to recompile runtime, for example).
Thousands of "anonymous" functions from VBA6.DLL that are not covered by any debug symbols and whose names and activity were absolutely unknown, are discovered and reverse-engineered, including lots of functions responsible for source code parsing, compilation:

Many of them are described this way (prototype, multiversionality info, short description, pseudocode):

(I am showing simplest one, because otherwise it wouldn't fit one screen. Also, it is in Russian, as you can see, but about half of docs are written in English)
Lots of internal structures are reversed and described:
Tons of documents describing how various internal components of Visual Basic work and are organized:
I am going to make all this information publicly available as part of VB-RCE project. However, I don't want to do that until it reaches some critical mass, which, I believe, it still hasn't reached. Not all tools are 100% polished. Not all parts of my TDRE workflow is automated or works as I wish.
However, over the years of reverse-engineering I made a number of interesting discoveries.
- Did you know about undocumented keys in VBP-file: C2Switches and LinkSwitches under [VBCompiler] section? You probably did, because I was the first who described it here in November 2011 and now you can google many mentions of VBCompiler section, but back in the days this was unique information.
- Did you know that one bit separates you from this kind of syntax?

- Did you know that there is hidden analogue of CallByName but for plain functions/API instead of object methods in VB? As well as a hidden basis for expression evaluation engine?
- Did you know that that there is undocumented mechanism allowing connecting from external software to VB IDE or to compiled VB project and tracing events (including step-by-step tracing, which, however, works only in IDE mode)?
- Do you know that a number of attempts to rename any variable (or any entity) is limited to 32K times due to the nature of how code entities are represented internally?

I have started a series of articles describing my explorations and reverse-engineering results. Unfortunately for English-speaking people, these articles are in Russian. It's called VB Internals and is published on VBStreets.
Currently there are only two ready articles:
The first one covers very important topic that Visual Basic is actually a hybrid of two products (Ruby and EB) glued together. It is very important to understand what Ruby and EB are, how they were born and where the boundaries between them lay prior to diving into reverse-engineering or any explorations. It should be considered an introduction article for the whole series.
The second one tries to evaluate number of C/C++/assembler source files (sources of VB6 itself) and the overall number of lines in these files basing on the information that can be obtained by extraction raw data from DBG/PDB files and consequent sophisticated postprocessing.
The third is upcoming: it will bring more details on how sources are organized and how various components of IDE/runtime is distributed across source files.
Here is the preliminary list of topics that I plan to highlight in the series. ( Google translate)
Hello firehacker,
Can you provide me a working c/c++ sample project for vb6 multithreading like you have have shown CreateBasicThread() in vbstreets.ru .
Thanks
-
Jan 13th, 2022, 08:45 PM
#56
Re: Instantiate internal class object with name in string
Hey smkperu, how about starting your own thread instead of hijacking mine from 4+ years ago???
Also, I'm not sure Firehacker spends much time around here. You'd probably be better off targeting your questions like this to The Trick (in your own thread).
Any software I post in these forums written by me is provided "AS IS" without warranty of any kind, expressed or implied, and permission is hereby granted, free of charge and without restriction, to any person obtaining a copy. To all, peace and happiness.
-
Jan 14th, 2022, 12:35 AM
#57
Addicted Member
Re: Instantiate internal class object with name in string
-
Jan 15th, 2022, 05:23 PM
#58
New Member
Re: Instantiate internal class object with name in string
 Originally Posted by Elroy
Also, I'm not sure Firehacker spends much time around here. You'd probably be better off targeting your questions like this to The Trick (in your own thread).
No, I get notifications via email, so I can react quickly on questions addresses to me asked in this thread. By the way, it's better to target questions relating to something previously posted on bbs.vbstreets.ru right there on bbs.vbstreets.ru Even if it is russian-speaking forum, it is absolutely okay if you write your post in English. Registration is still opened. Feel free to contact me if you have any problems with switching VBStreets UI from Russian to English. Someone have mentioned he had some issues with that.
 Originally Posted by smkperu
Hello firehacker,
Can you provide me a working c/c++ sample project for vb6 multithreading like you have have shown CreateBasicThread() in vbstreets.ru .
I don't understand clearly what do you mean by "sample project". Ten years ago I announced that contrary to the popular opinion that VB6 does not support multithreading at all (since using CreateThread() usually causes new thread to crash the whole process) under the hood MSVBVM is thread-safe and multithreading-aware at every step and it's only a question of how to create and initialize a new thread propeprly — and I discovered a way of proper thread creation and initialization.
On VBStreets we use the term "brick" to call ready-to-use module (or bunch of modules) which we post on VBStreets (in specific forum aka "Brick Factory") and which can be downloaded by people and plugged into some VB-project without any tweaks and modifications. Initially, I was planning to make a brick which brings support of multithreading into VB. However, I changed my mind very: implementing such thing purely in form of VB code would be rather hard and tricky. I decided to write CreateBasicThread in C/C++ in form of a separate DLL rather then writing it in VB as a simple VB module.
So, I announced that I am going to develop this DLL and publish it. This was called "Stable Multithreading Brick", even though it wasn't a "brick" in its initial sense anymore. I haven't planned to make this DLL open-source: not that I wanted to conceal a number of secrets and "know hows" (even though there was some), but to prevent a number of incompatible forks from 3rd party enthusiasts to appear.
At some point I make difficult decision to stop development of stable multithreading brick aka vbthrds.dll. The main and the only reason for that is the following fact:
To make things work properly my DLL needed to work with significant number of functions/variables/methods from MSVBVM which are neither exported nor live at constant locations (i.e. their addresses change with each new version of MSVBVM60.DLL). Functions and variables were living at arbitrary addresses (version-dependent), the machine code of that functions was changing slightly from version to version which was making it harder to find necessary functions by doing a so-called "signature search". Sizes of structure and offsets of fields of classes/structs were also changing from version to version. I had to write code that locate each function, each variable, determines version of MSVBVMxx.DLL and guess size of structs and offsets of fields accordingly.
I wasn't satisfied with the idea that my vbthrds.dll will come with "to use this library you must use that version of MSVBVM60.DLL and only that exactly version" warning. I was going to support ALL versions (builds, releases) of MSVBVM60.DLL that exist (so I collected all existing versions and examined them all). Furthermore, I was going to support MSVBM50.DLL for those who use VB5.
Some people love to use packers which embed MSVBVMxx.DLL into main EXE, and some people prefer to rename MSVBVMxx.DLL into something obscure and modify import table of the main EXE/DLL accordingly (to hide the fact they are using VB or I don't know what is their goal exactly). Given that facts, you should be prepared for the situation when multiple DIFFERENT versions of MSVBVM co-exist in the address space of our process. Even without these rare cases with packers or renaming of MSVBVM, you can simply get MSVBVM60.DLL and MSVBVM50.DLL both loaded into address space of your process if your main EXE (compiled using VB6) is using, for example, an OCX-control that is written in VB to (but compiled using VB5). Both may invoke CreateBasicThread: they are doing that independently, but from our point of view (vbthrds.dll) they use different runtimes (MSVBVM libraries). So, to be more robust, you can't just find all required internal entities. As an author of vbthrds.dll I had to write a mechanism which keeps track of all runtimes concurrently loaded into address space of our process and for each module (Windows terminology, not "module" in VB terminology) we need to locate all of required internal (not exported) entities and calculate all sizes and offsets.
Even with all that complex stuff, we still had a chance of Microsoft releasing a new version of MSVBVM60.DLL which my "multithreading brick" won't deal with. Because some minor but still significant change can broke my mechanisms responsible for locating variables/functions/fields offsets.
This work together with another initiative (known as "msvbvm61.dll" — an idea of patching original MSVBVM60.DLL and embedding new functions/classes/controls and adding support of Unicode for all embedded controls/functions) led me to the conclusion that all such things require referencing LOTS of internal MSVBVM's functions and vars (which are not explorted, again) and sophistiacted mechanisms required to locate all of them are so hard and exhausting to make and support (they need to be prepared to work with any of existing versions of MSVBVM, and there were more than 10 of builds) comparing to how simple it would be if you have original MSVBVM60.DLL sources and you can just link to any of that internal functions or add any field to any structure or just remove any line of code instad of patching/intercepting machine code.
So I decided to focus on a project which I referenced above as "VB-RCE". All the wishes and desires that was there around VB6 for years (full Unicode support, multithreading, new functions and embedded classes (like Collection class)) becomes way easy to make if you doing it at the level of source code rather than by patching/intercepting compiled binaries. Even if reverse engineering of MSVBVM60 seems to be extremely hard task, it opens up huge perspectives. And, yet again, you haven't to reverse/decompile 100% of code before you can do any (even simpliest) modification.
That doesn't cancels the fact that vbthrds.dll was good idea and I had working prototypes. I just stopped its development in the name of switching to more comprehensive approach.
But vbthrds.dll wasn't open-source and I haven't been finished. I can't remenber if I event posted semi-functional (alpha) builds of vbthrds.dll.
And you asking me for a sample C/C++ project. What do you mean? Do you want me to publish the whole source code of incomplete vbthrds.dll? Do you wan't me to post source code of CreateBasicThread function?
Here is source code of CreateBasicThread as it is with all its debug/temporary/raw snippets:
Code:
HANDLE WINAPI CreateBasicThread(LPSECURITY_ATTRIBUTES lpThreadAttr,
LPTHREAD_START_ROUTINE lpStartProc,
LPVOID lpAuxParam)
{
HMODULE hProjModule;
SMT_VBPROJECT_DESCRIPTOR* pProjDescriptor = NULL;
SMT_VBRUNTIME_DESCRIPTOR* pRtDescriptor = NULL;
//
// Определяем модуль проекта, которому принадлежит процедура.
//
hProjModule = PeFindContainerOfSymbol(lpStartProc);
if(hProjModule == NULL)
{
// TODO: Set Error code
return NULL;
}
//
// Просим менеджер кеша предоставить нам дескриптор для данного проекта.
// Менеджер кеша либо создаст новый дескриптор, либо вернёт уже существующий.
//
SmtCmProvideCacheEntriesForHmod(hProjModule,
&pProjDescriptor,
&pRtDescriptor);
if(pProjDescriptor == NULL)
{
// TODO: Set error code
return NULL;
}
//
// DBG: Патчим проджект, TODO: fix it
//
SMT_CTHREADPOOL_SFTABLE &pctp = pRtDescriptor->Internals.SFTables.CCThreadPool;
Project* pProj;
CThreadPool* pRby_ThreadPool = pRtDescriptor->Internals.Rby_ThreadPool;
pRby_ThreadPool;
pProj = (pRby_ThreadPool->*pctp.FindCachePepiProj)(pProjDescriptor->pProjInfo);
((DWORD*)pProj)[37] = 0;
SMT_START_PARAMETERS* pStartParams;
DWORD fdwSystemFlags;
HANDLE hNewThread;
DWORD iNewThreadId;
CONTEXT ntctx;
//
// Мы всегда создаём новый поток в замороженном состоянии, чтобы сформировать
// структуру SMT_START_PARAMETERS в его стеке. Это единственный способ передать
// потоку структуру так, чтобы потом не заботиться об её уничтожении.
//
// Мы не можем переложить ответственность за уничтожение этой структуры на новый
// поток, потому что нет гарантии, что его вообще когда-нибудь разморозят.
// Мы не можем сами заботиться об её уничтожении, потому что для этого потребовалось
// бы отслеживать терминацию всех потоков и держать большую таблицу указателей на
// ждущие уничтожения структуры.
//
fdwSystemFlags = CREATE_SUSPENDED; /* TODO: fix it */
hNewThread = CreateThread(lpThreadAttr,
0, /* TODO: Fix it */
SmtIntermThreadProc,
(LPVOID)SMT_SPB_FAULT_MARKER,
fdwSystemFlags,
&iNewThreadId);
if(!hNewThread) return hNewThread;
//
// Получаем контекст нового потока. Затем сдвигаем указатель стека так,
// чтобы там можно было разместить структуру. И заменяем контекст потока
// новым, изменённым.
//
ntctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
if(!GetThreadContext(hNewThread, &ntctx))
{
TerminateThread(hNewThread, 0);
CloseHandle(hNewThread);
return NULL;
}
ntctx.Esp -= sizeof(SMT_START_PARAMETERS);
pStartParams = (SMT_START_PARAMETERS*)ntctx.Esp;
//
// Win32 API — тупейшая обёртка над Native API, которая убивает кучу востребованных
// возможностей Native API. В частности, мы не можем передать создаваемому потоку
// структуру большого размера, мы можем только передать указатель на структуру,
// но саму структуру мы никак не можем «прикрепить» к потоку. Если бы CreateThread
// позволяла создать в новом стеке некий room, с заведомо известным адресом, который
// бы можно было передать в CreateThread в качестве lpParam... А так нам приходится
// искусственно воссоздавать такую функциональность. Методика основана на знании
// недокументированных особенностей функционирования ядра Windows, а именно функции
// BaseInitializeContext. Если она когда-нибудь изменится, то трюк не сработает,
// поэтому нужно проверить, работает ли BaseInitializeContext так же, как ожидается.
//
if(ntctx.Eax == (DWORD)SmtIntermThreadProc &&
ntctx.Ebx == SMT_SPB_FAULT_MARKER)
{
// BaseInitializeContext работает как ожидалось.
ntctx.Ebx = (DWORD)pStartParams;
}
else
{
//
// BaseInitializeContext работает иначе, а значит новому потоку самому придётся
// искать в своём стеке структуру SMT_START_PARAMETERS, потому что её адрес потоку
// передать никак не удалось. Формируем маркер для поиска...
//
SMT_START_PARAMETERS_MARKER* pSpbMarker;
ntctx.Esp -= sizeof(SMT_START_PARAMETERS_MARKER);
pSpbMarker = (SMT_START_PARAMETERS_MARKER*)ntctx.Esp;
*pSpbMarker = SmtStartParametersBlockMarker;
}
ntctx.Esp -= sizeof(DWORD) * 64; // Отступ ради безопасности
if(!SetThreadContext(hNewThread, &ntctx))
{
TerminateThread(hNewThread, 0);
CloseHandle(hNewThread);
return NULL;
}
//
// Теперь структура лежит на стеке созданного потока, и мы можем спокойно её
// заполнять стартовыми параметрами.
//
pStartParams->UserThreadProc = lpStartProc;
pStartParams->UserThreadParam = lpAuxParam;
pStartParams->Project = pProjDescriptor;
pStartParams->Runtime = pRtDescriptor;
// pStartParams->Flags = 0; /* TODO: Fix it */
BOOL bNeedTlsEmulation = TRUE;
if(bNeedTlsEmulation)
{
//DebugBreak();
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
EhbtlsAttachToThread(GetCurrentThread());
EhbtlsEnableSection(pProjDescriptor->TlsEmuSection);
}
//
// Теперь наконец даём новому потоку работать.
//
ResumeThread(hNewThread);
return hNewThread;
}
Comments are in Russian, sorry. I presume Google Translate will give poor results as it always does with comments within code.
Here is SmtIntermThreadProc which is a thunk to caller-supplied ThreadProc and is responsible for proper initialization of runtime library for a newly created thread from context of that new thread:
Code:
DWORD WINAPI SmtIntermThreadProc(LPVOID lpParameter)
{
DWORD ret;
SMT_START_PARAMETERS* psp;
CThreadPool* Rby_ThreadPool;
if(lpParameter == (LPVOID)SMT_SPB_FAULT_MARKER)
{
//
// Наш родитель не смог передать нам указатель на структуру со
// стартовыми параметрами из-за изменений в BaseInitializeContext.
// Ищем её в своём стеке вручную.
//
psp = SmtFindSpbOnThreadStack();
}
else
{
psp = (SMT_START_PARAMETERS*)lpParameter;
}
Rby_ThreadPool = psp->Runtime->Internals.Rby_ThreadPool;
SMT_CTHREADPOOL_SFTABLE& ctpm = psp->Runtime->Internals.SFTables.CCThreadPool;
BOOL bNeedTlsEmulation = TRUE;
EHBTLS_FRAME TlsEmuFrame;
if(bNeedTlsEmulation)
{
EhbtlsEnterThreadSafeRegionEx(&TlsEmuFrame, psp->Project->TlsEmuSection);
}
//
// Вызываем RbyThreadPool.InitDllAccess для инициализации контекста
// данного проекта для нового потока.
//
(Rby_ThreadPool->*ctpm.InitDllAccess)(psp->Project->pProjInfo, psp->Project->hModule);
//
// А теперь вызываем пользовательскую ThreadProc.
//
ret = psp->UserThreadProc(psp->UserThreadParam);
CThreadData* pMyData;
CMsoComponent* pMsoComp;
(Rby_ThreadPool->*ctpm.GetThreadData)(&pMyData);
if(pMyData)
{
pMyData->ThreadAction->ThreadType = VbExeThreadWorker;
*(HANDLE*)((PBYTE)pMyData->ThreadAction + 0x18) = CreateEvent(0, 0, 0, 0);
#ifndef SMT_SUPPORT_OLD_RUNTIMES
pMsoComp = pMyData->MsoComponent;
#else
pMsoComponent = *(CMsoComponent**)
((BYTE*)pMyData + psp->Runtime->Internals.offset_MsoCmpFldInThreadData);
#endif
if(pMsoComp)
{
(pMsoComp->*psp->Project->Runtime->Internals.SFTables.CCMsoComponent.PushMsgLoop)(-1);
}
}
//
// Деинициализируем контекст потока.
//
(Rby_ThreadPool->*ctpm.TerminateThread)();
if(bNeedTlsEmulation)
{
EhbtlsLeaveThreadSafeRegion(&TlsEmuFrame);
}
ExitThread(ret);
return ret; // Этого никогда не произойдёт
}
This two subroutines are only small part of the whole vbthrds.dll.
One of the cruicial components of EHBTLS: exception-handling based TLS. Global variables of single-threaded application are allocated on data section of executable image. However, for multithreaded VB program it isn't acceptable: each thread must have its own copy of the set of all global variables. Since project types such as "ActiveX DLL", "ActiveX Control" and "ActiveX EXE" implies that VB-code can by run in context of multiple different threads, VB (from IDE/compiler point of view) offers "Threading model" option under "Project settings", which can be either "Single Threaded" or "Apartament threaded", and when project is configured to use "Apartament threaded", global variables are allocated either in data section (same as when compiling Single Threaded projects) for main thread of multithreaded project, or in per-thread block of memory for any thread other than main thread. To locate proper instance of global variable VB-code calls vbaAptOffset which returns offset from variable's address in data section to its actual location in per-thread memory block. For the main thread this offset is 0, but for other threads it is a non-zero value so that adding this offset to a address of the variable within data section gives an actual address of this global var instance for a particular thread.
But VB6 IDE and compiler does not allow you to set "Apartament threading" model for projects of type "Standard EXE". Thus, trying to spawn multiple threads within Standard EXE project will lead to a situation where all global variables are shared between all threads. This is barely acceptable for Byte/Integer/Long variables, but is absolutely unacceptable for objects and Variants. To overcome this, my library used SEH/VEH to detect reads and writes from/to global variables and redirect it to proper per-thread memory block containing per-thread copies of all global variables of the project.
Without this you need to be very careful and avoid using global variables. But you can't avoid it completely: App, Forms, Printer, Printers, Clipboard, Screen and so on are actually members of an app-global-object (you can find a class named "Global" in Object Browser) which is referenced via unnamed global variable. So need to emulate TLS for "Standard EXE" projects which might use native TLS facilities of VB but which are actually don't use it due to limitations of IDE/compiler.
Yet another part of vbthds.dll is SMS: it has nothing to do with mobile phones, it stands for Sub Main suppression. If your project has "Startup Object" = "Sub Main" (project properties), Sub Main() gets called each time you create a new thread using my DLL, which is usually not what you want. You can't easily determine from the Sub Main() whether it was called because a program startup or it is called as a side-effect of spawning additional thread. So there was special trickery to suppress invocation of "Sub Main" when new threads were created and initialized via my DLL.
Yet again, these two are great examples of how complex the simple problem becomes when you have to solve it on the wrong level. Having access to the source code of VB (the whole product including IDE/compiler and runtime) and unlocking "Theading Model" for "Standard EXE" or just avoiding unwanted invocation of Main() by simple if-block within MSVBVM code would be much simplier than emulating TLS for complete binary compiled without TLS support or suppressing a Sub Main invocation without patching MSVBVM project startup logic.
It's like having a hand or any other organ: when you born you get your hands "for free" and you take it for granted. But if you loose your hand, it costs you enormous amount of effort and money and even the best of the best prosthetic arms which are very expensive can bring you only limited amount of what you lost.
Last edited by firehacker; Jan 16th, 2022 at 05:18 AM.
-
Jan 16th, 2022, 12:05 PM
#59
Addicted Member
Re: Instantiate internal class object with name in string
 Originally Posted by firehacker
No, I get notifications via email, so I can react quickly on questions addresses to me asked in this thread. By the way, it's better to target questions relating to something previously posted on bbs.vbstreets.ru right there on bbs.vbstreets.ru  Even if it is russian-speaking forum, it is absolutely okay if you write your post in English. Registration is still opened. Feel free to contact me if you have any problems with switching VBStreets UI from Russian to English. Someone have mentioned he had some issues with that.
I don't understand clearly what do you mean by "sample project". Ten years ago I announced that contrary to the popular opinion that VB6 does not support multithreading at all (since using CreateThread() usually causes new thread to crash the whole process) under the hood MSVBVM is thread-safe and multithreading-aware at every step and it's only a question of how to create and initialize a new thread propeprly — and I discovered a way of proper thread creation and initialization.
On VBStreets we use the term "brick" to call ready-to-use module (or bunch of modules) which we post on VBStreets (in specific forum aka "Brick Factory") and which can be downloaded by people and plugged into some VB-project without any tweaks and modifications. Initially, I was planning to make a brick which brings support of multithreading into VB. However, I changed my mind very: implementing such thing purely in form of VB code would be rather hard and tricky. I decided to write CreateBasicThread in C/C++ in form of a separate DLL rather then writing it in VB as a simple VB module.
So, I announced that I am going to develop this DLL and publish it. This was called "Stable Multithreading Brick", even though it wasn't a "brick" in its initial sense anymore. I haven't planned to make this DLL open-source: not that I wanted to conceal a number of secrets and "know hows" (even though there was some), but to prevent a number of incompatible forks from 3rd party enthusiasts to appear.
At some point I make difficult decision to stop development of stable multithreading brick aka vbthrds.dll. The main and the only reason for that is the following fact:
To make things work properly my DLL needed to work with significant number of functions/variables/methods from MSVBVM which are neither exported nor live at constant locations (i.e. their addresses change with each new version of MSVBVM60.DLL). Functions and variables were living at arbitrary addresses (version-dependent), the machine code of that functions was changing slightly from version to version which was making it harder to find necessary functions by doing a so-called "signature search". Sizes of structure and offsets of fields of classes/structs were also changing from version to version. I had to write code that locate each function, each variable, determines version of MSVBVMxx.DLL and guess size of structs and offsets of fields accordingly.
I wasn't satisfied with the idea that my vbthrds.dll will come with " to use this library you must use that version of MSVBVM60.DLL and only that exactly version" warning. I was going to support ALL versions (builds, releases) of MSVBVM60.DLL that exist (so I collected all existing versions and examined them all). Furthermore, I was going to support MSVBM50.DLL for those who use VB5.
Some people love to use packers which embed MSVBVMxx.DLL into main EXE, and some people prefer to rename MSVBVMxx.DLL into something obscure and modify import table of the main EXE/DLL accordingly (to hide the fact they are using VB or I don't know what is their goal exactly). Given that facts, you should be prepared for the situation when multiple DIFFERENT versions of MSVBVM co-exist in the address space of our process. Even without these rare cases with packers or renaming of MSVBVM, you can simply get MSVBVM60.DLL and MSVBVM50.DLL both loaded into address space of your process if your main EXE (compiled using VB6) is using, for example, an OCX-control that is written in VB to (but compiled using VB5). Both may invoke CreateBasicThread: they are doing that independently, but from our point of view (vbthrds.dll) they use different runtimes (MSVBVM libraries). So, to be more robust, you can't just find all required internal entities. As an author of vbthrds.dll I had to write a mechanism which keeps track of all runtimes concurrently loaded into address space of our process and for each module (Windows terminology, not "module" in VB terminology) we need to locate all of required internal (not exported) entities and calculate all sizes and offsets.
Even with all that complex stuff, we still had a chance of Microsoft releasing a new version of MSVBVM60.DLL which my "multithreading brick" won't deal with. Because some minor but still significant change can broke my mechanisms responsible for locating variables/functions/fields offsets.
This work together with another initiative (known as " msvbvm61.dll" — an idea of patching original MSVBVM60.DLL and embedding new functions/classes/controls and adding support of Unicode for all embedded controls/functions) led me to the conclusion that all such things require referencing LOTS of internal MSVBVM's functions and vars (which are not explorted, again) and sophistiacted mechanisms required to locate all of them are so hard and exhausting to make and support (they need to be prepared to work with any of existing versions of MSVBVM, and there were more than 10 of builds) comparing to how simple it would be if you have original MSVBVM60.DLL sources and you can just link to any of that internal functions or add any field to any structure or just remove any line of code instad of patching/intercepting machine code.
So I decided to focus on a project which I referenced above as "VB-RCE". All the wishes and desires that was there around VB6 for years (full Unicode support, multithreading, new functions and embedded classes (like Collection class)) becomes way easy to make if you doing it at the level of source code rather than by patching/intercepting compiled binaries. Even if reverse engineering of MSVBVM60 seems to be extremely hard task, it opens up huge perspectives. And, yet again, you haven't to reverse/decompile 100% of code before you can do any (even simpliest) modification.
That doesn't cancels the fact that vbthrds.dll was good idea and I had working prototypes. I just stopped its development in the name of switching to more comprehensive approach.
But vbthrds.dll wasn't open-source and I haven't been finished. I can't remenber if I event posted semi-functional (alpha) builds of vbthrds.dll.
And you asking me for a sample C/C++ project. What do you mean? Do you want me to publish the whole source code of incomplete vbthrds.dll? Do you wan't me to post source code of CreateBasicThread function?
Here is source code of CreateBasicThread as it is with all its debug/temporary/raw snippets:
Code:
HANDLE WINAPI CreateBasicThread(LPSECURITY_ATTRIBUTES lpThreadAttr,
LPTHREAD_START_ROUTINE lpStartProc,
LPVOID lpAuxParam)
{
HMODULE hProjModule;
SMT_VBPROJECT_DESCRIPTOR* pProjDescriptor = NULL;
SMT_VBRUNTIME_DESCRIPTOR* pRtDescriptor = NULL;
//
// Определяем модуль проекта, которому принадлежит процедура.
//
hProjModule = PeFindContainerOfSymbol(lpStartProc);
if(hProjModule == NULL)
{
// TODO: Set Error code
return NULL;
}
//
// Просим менеджер кеша предоставить нам дескриптор для данного проекта.
// Менеджер кеша либо создаст новый дескриптор, либо вернёт уже существующий.
//
SmtCmProvideCacheEntriesForHmod(hProjModule,
&pProjDescriptor,
&pRtDescriptor);
if(pProjDescriptor == NULL)
{
// TODO: Set error code
return NULL;
}
//
// DBG: Патчим проджект, TODO: fix it
//
SMT_CTHREADPOOL_SFTABLE &pctp = pRtDescriptor->Internals.SFTables.CCThreadPool;
Project* pProj;
CThreadPool* pRby_ThreadPool = pRtDescriptor->Internals.Rby_ThreadPool;
pRby_ThreadPool;
pProj = (pRby_ThreadPool->*pctp.FindCachePepiProj)(pProjDescriptor->pProjInfo);
((DWORD*)pProj)[37] = 0;
SMT_START_PARAMETERS* pStartParams;
DWORD fdwSystemFlags;
HANDLE hNewThread;
DWORD iNewThreadId;
CONTEXT ntctx;
//
// Мы всегда создаём новый поток в замороженном состоянии, чтобы сформировать
// структуру SMT_START_PARAMETERS в его стеке. Это единственный способ передать
// потоку структуру так, чтобы потом не заботиться об её уничтожении.
//
// Мы не можем переложить ответственность за уничтожение этой структуры на новый
// поток, потому что нет гарантии, что его вообще когда-нибудь разморозят.
// Мы не можем сами заботиться об её уничтожении, потому что для этого потребовалось
// бы отслеживать терминацию всех потоков и держать большую таблицу указателей на
// ждущие уничтожения структуры.
//
fdwSystemFlags = CREATE_SUSPENDED; /* TODO: fix it */
hNewThread = CreateThread(lpThreadAttr,
0, /* TODO: Fix it */
SmtIntermThreadProc,
(LPVOID)SMT_SPB_FAULT_MARKER,
fdwSystemFlags,
&iNewThreadId);
if(!hNewThread) return hNewThread;
//
// Получаем контекст нового потока. Затем сдвигаем указатель стека так,
// чтобы там можно было разместить структуру. И заменяем контекст потока
// новым, изменённым.
//
ntctx.ContextFlags = CONTEXT_CONTROL | CONTEXT_INTEGER;
if(!GetThreadContext(hNewThread, &ntctx))
{
TerminateThread(hNewThread, 0);
CloseHandle(hNewThread);
return NULL;
}
ntctx.Esp -= sizeof(SMT_START_PARAMETERS);
pStartParams = (SMT_START_PARAMETERS*)ntctx.Esp;
//
// Win32 API — тупейшая обёртка над Native API, которая убивает кучу востребованных
// возможностей Native API. В частности, мы не можем передать создаваемому потоку
// структуру большого размера, мы можем только передать указатель на структуру,
// но саму структуру мы никак не можем «прикрепить» к потоку. Если бы CreateThread
// позволяла создать в новом стеке некий room, с заведомо известным адресом, который
// бы можно было передать в CreateThread в качестве lpParam... А так нам приходится
// искусственно воссоздавать такую функциональность. Методика основана на знании
// недокументированных особенностей функционирования ядра Windows, а именно функции
// BaseInitializeContext. Если она когда-нибудь изменится, то трюк не сработает,
// поэтому нужно проверить, работает ли BaseInitializeContext так же, как ожидается.
//
if(ntctx.Eax == (DWORD)SmtIntermThreadProc &&
ntctx.Ebx == SMT_SPB_FAULT_MARKER)
{
// BaseInitializeContext работает как ожидалось.
ntctx.Ebx = (DWORD)pStartParams;
}
else
{
//
// BaseInitializeContext работает иначе, а значит новому потоку самому придётся
// искать в своём стеке структуру SMT_START_PARAMETERS, потому что её адрес потоку
// передать никак не удалось. Формируем маркер для поиска...
//
SMT_START_PARAMETERS_MARKER* pSpbMarker;
ntctx.Esp -= sizeof(SMT_START_PARAMETERS_MARKER);
pSpbMarker = (SMT_START_PARAMETERS_MARKER*)ntctx.Esp;
*pSpbMarker = SmtStartParametersBlockMarker;
}
ntctx.Esp -= sizeof(DWORD) * 64; // Отступ ради безопасности
if(!SetThreadContext(hNewThread, &ntctx))
{
TerminateThread(hNewThread, 0);
CloseHandle(hNewThread);
return NULL;
}
//
// Теперь структура лежит на стеке созданного потока, и мы можем спокойно её
// заполнять стартовыми параметрами.
//
pStartParams->UserThreadProc = lpStartProc;
pStartParams->UserThreadParam = lpAuxParam;
pStartParams->Project = pProjDescriptor;
pStartParams->Runtime = pRtDescriptor;
// pStartParams->Flags = 0; /* TODO: Fix it */
BOOL bNeedTlsEmulation = TRUE;
if(bNeedTlsEmulation)
{
//DebugBreak();
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
LoadLibrary("vbthrds.dll");
EhbtlsAttachToThread(GetCurrentThread());
EhbtlsEnableSection(pProjDescriptor->TlsEmuSection);
}
//
// Теперь наконец даём новому потоку работать.
//
ResumeThread(hNewThread);
return hNewThread;
}
Comments are in Russian, sorry. I presume Google Translate will give poor results as it always does with comments within code.
Here is SmtIntermThreadProc which is a thunk to caller-supplied ThreadProc and is responsible for proper initialization of runtime library for a newly created thread from context of that new thread:
Code:
DWORD WINAPI SmtIntermThreadProc(LPVOID lpParameter)
{
DWORD ret;
SMT_START_PARAMETERS* psp;
CThreadPool* Rby_ThreadPool;
if(lpParameter == (LPVOID)SMT_SPB_FAULT_MARKER)
{
//
// Наш родитель не смог передать нам указатель на структуру со
// стартовыми параметрами из-за изменений в BaseInitializeContext.
// Ищем её в своём стеке вручную.
//
psp = SmtFindSpbOnThreadStack();
}
else
{
psp = (SMT_START_PARAMETERS*)lpParameter;
}
Rby_ThreadPool = psp->Runtime->Internals.Rby_ThreadPool;
SMT_CTHREADPOOL_SFTABLE& ctpm = psp->Runtime->Internals.SFTables.CCThreadPool;
BOOL bNeedTlsEmulation = TRUE;
EHBTLS_FRAME TlsEmuFrame;
if(bNeedTlsEmulation)
{
EhbtlsEnterThreadSafeRegionEx(&TlsEmuFrame, psp->Project->TlsEmuSection);
}
//
// Вызываем RbyThreadPool.InitDllAccess для инициализации контекста
// данного проекта для нового потока.
//
(Rby_ThreadPool->*ctpm.InitDllAccess)(psp->Project->pProjInfo, psp->Project->hModule);
//
// А теперь вызываем пользовательскую ThreadProc.
//
ret = psp->UserThreadProc(psp->UserThreadParam);
CThreadData* pMyData;
CMsoComponent* pMsoComp;
(Rby_ThreadPool->*ctpm.GetThreadData)(&pMyData);
if(pMyData)
{
pMyData->ThreadAction->ThreadType = VbExeThreadWorker;
*(HANDLE*)((PBYTE)pMyData->ThreadAction + 0x18) = CreateEvent(0, 0, 0, 0);
#ifndef SMT_SUPPORT_OLD_RUNTIMES
pMsoComp = pMyData->MsoComponent;
#else
pMsoComponent = *(CMsoComponent**)
((BYTE*)pMyData + psp->Runtime->Internals.offset_MsoCmpFldInThreadData);
#endif
if(pMsoComp)
{
(pMsoComp->*psp->Project->Runtime->Internals.SFTables.CCMsoComponent.PushMsgLoop)(-1);
}
}
//
// Деинициализируем контекст потока.
//
(Rby_ThreadPool->*ctpm.TerminateThread)();
if(bNeedTlsEmulation)
{
EhbtlsLeaveThreadSafeRegion(&TlsEmuFrame);
}
ExitThread(ret);
return ret; // Этого никогда не произойдёт
}
This two subroutines are only small part of the whole vbthrds.dll.
One of the cruicial components of EHBTLS: exception-handling based TLS. Global variables of single-threaded application are allocated on data section of executable image. However, for multithreaded VB program it isn't acceptable: each thread must have its own copy of the set of all global variables. Since project types such as "ActiveX DLL", "ActiveX Control" and "ActiveX EXE" implies that VB-code can by run in context of multiple different threads, VB (from IDE/compiler point of view) offers "Threading model" option under "Project settings", which can be either "Single Threaded" or "Apartament threaded", and when project is configured to use "Apartament threaded", global variables are allocated either in data section (same as when compiling Single Threaded projects) for main thread of multithreaded project, or in per-thread block of memory for any thread other than main thread. To locate proper instance of global variable VB-code calls vbaAptOffset which returns offset from variable's address in data section to its actual location in per-thread memory block. For the main thread this offset is 0, but for other threads it is a non-zero value so that adding this offset to a address of the variable within data section gives an actual address of this global var instance for a particular thread.
But VB6 IDE and compiler does not allow you to set "Apartament threading" model for projects of type "Standard EXE". Thus, trying to spawn multiple threads within Standard EXE project will lead to a situation where all global variables are shared between all threads. This is barely acceptable for Byte/Integer/Long variables, but is absolutely unacceptable for objects and Variants. To overcome this, my library used SEH/VEH to detect reads and writes from/to global variables and redirect it to proper per-thread memory block containing per-thread copies of all global variables of the project.
Without this you need to be very careful and avoid using global variables. But you can't avoid it completely: App, Forms, Printer, Printers, Clipboard, Screen and so on are actually members of an app-global-object (you can find a class named "Global" in Object Browser) which is referenced via unnamed global variable. So need to emulate TLS for "Standard EXE" projects which might use native TLS facilities of VB but which are actually don't use it due to limitations of IDE/compiler.
Yet another part of vbthds.dll is SMS: it has nothing to do with mobile phones, it stands for Sub Main suppression. If your project has "Startup Object" = "Sub Main" (project properties), Sub Main() gets called each time you create a new thread using my DLL, which is usually not what you want. You can't easily determine from the Sub Main() whether it was called because a program startup or it is called as a side-effect of spawning additional thread. So there was special trickery to suppress invocation of "Sub Main" when new threads were created and initialized via my DLL.
Yet again, these two are great examples of how complex the simple problem becomes when you have to solve it on the wrong level. Having access to the source code of VB (the whole product including IDE/compiler and runtime) and unlocking "Theading Model" for "Standard EXE" or just avoiding unwanted invocation of Main() by simple if-block within MSVBVM code would be much simplier than emulating TLS for complete binary compiled without TLS support or suppressing a Sub Main invocation without patching MSVBVM project startup logic.
It's like having a hand or any other organ: when you born you get your hands "for free" and you take it for granted. But if you loose your hand, it costs you enormous amount of effort and money and even the best of the best prosthetic arms which are very expensive can bring you only limited amount of what you lost.
Hello FireHacker,
Please find my reply at https://www.vbforums.com/showthread....91#post5552291 at #2
Thanks
-
Jan 16th, 2022, 12:18 PM
#60
Re: Instantiate internal class object with name in string
Last edited by xiaoyao; Apr 16th, 2023 at 07:21 AM.
Reason: PLEASE DELETE THIS
-
Apr 16th, 2023, 06:47 AM
#61
Re: Instantiate internal class object with name in string
get ALL class name string
Code:
Public Declare Function lstrcpynA Lib "kernel32" (ByVal lpString1 As String, ByVal lpString2 As Long, ByVal iMaxLength As Long) As Long
Private Declare Function lstrlenA Lib "kernel32" (ByVal lpData As Long) As Long
Function GetStrFromPtr(lpszName As Long)
'GOOD
Dim STR1 As String, Len1 As Long, R As Long, X As Long
Len1 = lstrlenA(lpszName)
STR1 = Space(Len1 + 1)
R = lstrcpynA(STR1, lpszName, Len1 + 1)
X = InStr(STR1, Chr(0))
If X > 0 Then STR1 = Left(STR1, X - 1)
GetStrFromPtr = STR1
End Function
Code:
Function GetAllClassOnRun() As String()
' Only for Executable.
'
' lpObjInfo is a returned argument.
' Function returns true if successful.
Dim ClassList() As String
Static Modules() As NameBasedObjectFactory.MODDESCRTBL_ENTRY
Static bModulesSet As Boolean
Dim i As Long
'
#If ALSO_USERCONTROLS Then
Const mfBadFlags As Long = mfUserControl
#Else
Const mfBadFlags As Long = 0
#End If
'
If Not bModulesSet Then
ReDim Modules(0)
If LoadDescriptorsTable(Modules) Then
bModulesSet = True
Else
Exit Function
End If
End If
'
' We are looking for a descriptor corresponding to the specified class.
Dim ID As Long
ReDim ClassList(UBound(Modules) - LBound(Modules))
For i = LBound(Modules) To UBound(Modules)
With Modules(i)
ClassList(ID) = GetStrByPtr(.lpszName)
End With
ID = ID + 1
Next i
GetAllClassOnRun = ClassList()
End Function
Last edited by xiaoyao; Apr 16th, 2023 at 07:21 AM.
-
Jan 26th, 2025, 11:48 AM
#62
Re: Instantiate internal class object with name in string
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|