Page 1 of 2 12 LastLast
Results 1 to 40 of 44

Thread: [VB6] - Module for working with multithreading.

  1. #1

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015
    Posts
    1,590

    [VB6] - Module for working with multithreading.

    Hello everyone!

    I present the module for working with multithreading in VB6 for Standard EXE projects. This module is based on this solution with some bugfixing and the new functionality is added. The module doesn't require any additional dependencies and type libraries, works as in the IDE (all the functions work in the main thread) as in the compiled form.


    To start working with the module, you need to call the Initialize function, which initializes the necessary data (it initializes the critical sections for exclusive access to the heaps of marshalinig and threads, modifies VBHeader (here is description), allocates a TLS slot for passing the parameters to the thread).

    The main function of thread creation is vbCreateThread, which is an analog of the CreateThread function.

    Code:
    ' // Create a new thread
    Public Function vbCreateThread(ByVal lpThreadAttributes As Long, _
                                   ByVal dwStackSize As Long, _
                                   ByVal lpStartAddress As Long, _
                                   ByVal lpParameter As Long, _
                                   ByVal dwCreationFlags As Long, _
                                   ByRef lpThreadId As Long, _
                                   Optional ByVal bIDEInSameThread As Boolean = True) As Long
    The function creates a thread and calls the function passed in the lpStartAddress parameter with the lpParameter parameter.
    In the IDE, the call is reduced to a simple call by the pointer implemented through DispCallFunc. In the compiled form, this function works differently. Because a thread requires initialization of project-specific data and initialization of the runtime, the parameters passed to lpStartAddress and lpParameter are temporarily stored into the heap by the PrepareData function, and the thread is created in the ThreadProc function, which immediately deals with the initialization and calling of the user-defined function with the user parameter. This function creates a copy of the VBHeader structure via CreateVBHeaderCopy and changes the public variable placement data in the VbPublicObjectDescriptor.lpPublicBytes, VbPublicObjectDescriptor.lpStaticBytes structures (BTW it wasn't implemented in the previous version) so that global variables are not affected during initialization. Further, VBDllGetClassObject calls the FakeMain function (whose address is written to the modified VBHeader structure). To transfer user parameters, it uses a TLS slot (since Main function doesn't accept parameters, details here). In FakeMain, parameters are directly extracted from TLS and a user procedure is called. The return value of the function is also passed back through TLS. There is one interesting point related to the copy of the header that wasn't included in the previous version. Because the runtime uses the header after the thread ends (with DLL_THREAD_DETACH), we can't release the header in the ThreadProc procedure, therefore there will be a memory leak. To prevent the memory leaks, the heap of fixed size is used, the headers aren't cleared until there is a free memory in this heap. As soon as the memory ends (and it's allocated in the CreateVBHeaderCopy function), resources are cleared. The first DWORD of header actually stores the ID of the thread which it was created in and the FreeUnusedHeaders function checks all the headers in the heap. If a thread is completed, the memory is freed (although the ID can be repeated, but this doesn't play a special role, since in any case there will be a free memory in the heap and if the header isn't freed in one case, it will be released later). Due to the fact that the cleanup process can be run immediately from several threads, access to the cleanup is shared by the critical section tLockHeap.tWinApiSection and if some thread is already cleaning up the memory the function will return True which means that the calling thread should little bit waits and the memory will be available.

    The another feature of the module is the ability to initialize the runtime and the project and call the callback function. This can be useful for callback functions that can be called in the context of an arbitrary thread (for example, InternetStatusCallback). To do this, use the InitCurrentThreadAndCallFunction and InitCurrentThreadAndCallFunctionIDEProc functions. The first one is used in the compiled application and takes the address of the callback function that will be called after the runtime initialization, as well as the parameter to be passed to this function. The address of the first parameter is passed to the callback procedure to refer to it in the user procedure:

    Code:
    ' // This function is used in compiled form
    Public Function CallbackProc( _
                    ByVal lThreadId As Long, _
                    ByVal sKey As String, _
                    ByVal fTimeFromLastTick As Single) As Long
        ' // Init runtime and call CallBackProc_user with VarPtr(lThreadId) parameter
        InitCurrentThreadAndCallFunction AddressOf CallBackProc_user, VarPtr(lThreadId), CallbackProc
    End Function
    
    ' // Callback function is called by runtime/window proc (in IDE)
    Public Function CallBackProc_user( _
                    ByRef tParam As tCallbackParams) As Long
    
    End Function
    CallBackProc_user will be called with the initialized runtime.

    This function doesn't work in the IDE because in the IDE everything works in the main thread. For debugging in the IDE the function InitCurrentThreadAndCallFunctionIDEProc is used which returns the address of the assembler thunk that translates the call to the main thread and calls the user function in the context of the main thread. This function takes the address of the user's callback function and the size of the parameters in bytes. It always passes the address of the first parameter as a parameter of a user-defined function. I'll tell you a little more about the work of this approach in the IDE. To translate a call from the calling thread to the main thread it uses a message-only window. This window is created by calling the InitializeMessageWindow function. The first call creates a WindowProc procedure with the following code:

    Code:
        CMP DWORD [ESP+8], WM_ONCALLBACK
        JE SHORT L
        JMP DefWindowProcW
    L:  PUSH DWORD PTR SS:[ESP+10]
        CALL DWORD PTR SS:[ESP+10]
        RETN 10
    As you can see from the code, this procedure "listens" to the WM_ONCALLBACK message which contains the parameter wParam - the function address, and in the lParam parameters. Upon receiving this message it calls this procedure with this parameter, the remaining messages are ignored. This message is sent just by the assembler thunk from the caller thread. Futher, a window is created and the handle of this window and the code heap are stored into the data of the window class. This is used to avoid a memory leak in the IDE because if the window class is registered once, then these parameters can be obtained in any debugging session. The callback function is generated in InitCurrentThreadAndCallFunctionIDEProc, but first it's checked whether the same callback procedure has already been created (in order to don't create the same thunk). The thunk has the following code:

    Code:
    LEA EAX, [ESP+4]
    PUSH EAX
    PUSH pfnCallback
    PUSH WM_ONCALLBACK
    PUSH hMsgWindow
    Call SendMessageW
    RETN lParametersSize
    As you can see from the code, during calling a callback function, the call is transmitted via SendMessage to the main thread. The lParametersSize parameter is used to correctly restore the stack.

    The next feature of the module is the creation of objects in a separate thread, and you can create them as private objects (the method is based on the code of the NameBasedObjectFactory by firehacker module) as public ones. To create the project classes use the CreatePrivateObjectByNameInNewThread function and for ActiveX-public classes CreateActiveXObjectInNewThread and CreateActiveXObjectInNewThread2 ones. Before creating instances of the project classes you must first enable marshaling of these objects by calling the EnablePrivateMarshaling function. These functions accept the class identifier (ProgID / CLSID for ActiveX and the name for the project classes) and the interface identifier (IDispatch / Object is used by default). If the function is successfully called a marshaled object and an asynchronous call ID are returned. For the compiled version this is the ID of thread for IDE it's a pointer to the object. Objects are created and "live" in the ActiveXThreadProc function. The life of objects is controlled through the reference count (when it is equal to 1 it means only ActiveXThreadProc refers to the object and you can delete it and terminate the thread).
    You can call the methods either synchronously - just call the method as usual or asynchronously - using the AsynchDispMethodCall procedure. This procedure takes an asynchronous call ID, a method name, a call type, an object that receives the call notification, a notification method name and the list of parameters. The procedure copies the parameters to the temporary memory, marshals the notification object, and sends the data to the object's thread via WM_ASYNCH_CALL. It should be noted that marshaling of parameters isn't supported right now therefore it's necessary to transfer links to objects with care. If you want to marshal an object reference you should use a synchronous method to marshal the objects and then call the asynchronous method. The procedure is returned immediately. In the ActiveXThreadProc thread the data is retrieved and a synchronous call is made via MakeAsynchCall. Everything is simple, CallByName is called for the thread object and CallByName for notification. The notification method has the following prototype:

    Code:
    Public Sub CallBack (ByVal vRet As Variant)
    , where vRet accepts the return value of the method.

    The following functions are intended for marshaling: Marshal, Marshal2, UnMarshal, FreeMarshalData. The first one creates information about the marshaling (Proxy) of the interface and puts it into the stream (IStream) that is returned. It accepts the interface identifier in the pInterface parameter (IDispatch / Object by default). The UnMarshal function, on the contrary, receives a stream and creates a Proxy object based on the information in the stream. Optionally, you can release the thread object. Marshal2 does the same thing as Marshal except that it allows you to create a Proxy object many times in different threads. FreeMarshalData releases the data and the stream accordingly.
    If, for example, you want to transfer a reference to an object between two threads, it is enough to call the Marshal / UnMarshal pair in the thread which created the object and in the thread that receives the link respectively. In another case, if for example there is the one global object and you need to pass a reference to it to the multiple threads (for example, the logging object), then Marshal2 is called in the object thread, and UnMarshal with the bReleaseStream parameter is set to False is called in client threads. When the data is no longer needed, FreeMarshalData is called.

    The WaitForObjectThreadCompletion function is designed to wait for the completion of the object thread and receives the ID of the asynchronous call. It is desirable to call this function always at the end of the main process because an object thread can somehow interact with the main thread and its objects (for example, if the object thread has a marshal link to the interface of the main thread).

    The SuspendResume function is designed to suspend/resume the object's thread; bSuspend determines whether to sleep or resume the thread.

    In addition, there are also several examples in the attacment of working with module:
    1. Callback - the project demonstrates the work with the callback-function periodically called in the different threads. Also, there is an additional project of native dll (on VB6) which calls the function periodically in the different threads;
    2. JuliaSet - the Julia fractal generation in the several threads (user-defined);
    3. CopyProgress - Copy the folder in a separate thread with the progress of the copy;
    4. PublicMarshaling - Creating public objects (Dictionary) in the different threads and calling their methods (synchronously / asynchronously);
    5. PrivateMarshaling - Creating private objects in different threads and calling their methods (synchronously / asynchronously);
    6. MarshalUserInterface - Creating private objects in different threads and calling their methods (synchronously / asynchronously) based on user interfaces (contains tlb and Reg-Free manifest).
    7. InitProjectContextDll - Initialization of the runtime in an ActiveX DLL and call the exported function from the different threads. Setup callbacks to the executable.
    8. InternetStatusCallback - IternetStatusCallback usage in VB6. Async file downloading.


    The module is poorly tested so bugs are possible. I would be very glad to any bug-reports, wherever possible I will correct them.
    Thank you all for attention!

    Project on GitHub.
    Best Regards,
    The trick.

  2. #2
    Hyperactive Member
    Join Date
    Aug 2016
    Posts
    411

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    Hello everyone!

    I present the module for working with multithreading on VB6 for Standard EXE projects. This module is based on this solution with some bugfixing and the new functionality is added. The module doesn't require any additional dependencies and type libraries, works as in the IDE (all the functions work in the main thread) as in the compiled form.


    To start working with the module, you need to call the Initialize function, which initializes the necessary data (it initializes the critical sections for exclusive access to the heaps of marshalinig and threads, modifies VBHeader (here is description), allocates a TLS slot for passing the parameters to the thread).

    The main function of thread creation is vbCreateThread, which is an analog of the CreateThread function.

    Code:
    ' // Create a new thread
    Public Function vbCreateThread(ByVal lpThreadAttributes As Long, _
                                   ByVal dwStackSize As Long, _
                                   ByVal lpStartAddress As Long, _
                                   ByVal lpParameter As Long, _
                                   ByVal dwCreationFlags As Long, _
                                   ByRef lpThreadId As Long, _
                                   Optional ByVal bIDEInSameThread As Boolean = True) As Long
    The function creates a thread and calls the function passed in the lpStartAddress parameter with the lpParameter parameter.
    In the IDE, the call is reduced to a simple call by the pointer implemented through DispCallFunc. In the compiled form, this function works differently. Because a thread requires initialization of project-specific data and initialization of the runtime, the parameters passed to lpStartAddress and lpParameter are temporarily stored into the heap by the PrepareData function, and the thread is created in the ThreadProc function, which immediately deals with the initialization and calling of the user-defined function with the user parameter. This function creates a copy of the VBHeader structure via CreateVBHeaderCopy and changes the public variable placement data in the VbPublicObjectDescriptor.lpPublicBytes, VbPublicObjectDescriptor.lpStaticBytes structures (BTW it wasn't implemented in the previous version) so that global variables are not affected during initialization. Further, VBDllGetClassObject calls the FakeMain function (whose address is written to the modified VBHeader structure). To transfer user parameters, it uses a TLS slot (since Main function doesn't accept parameters, details here). In FakeMain, parameters are directly extracted from TLS and a user procedure is called. The return value of the function is also passed back through TLS. There is one interesting point related to the copy of the header that wasn't included in the previous version. Because the runtime uses the header after the thread ends (with DLL_THREAD_DETACH), we can't release the header in the ThreadProc procedure, therefore there will be a memory leak. To prevent the memory leaks, the heap of fixed size is used, the headers aren't cleared until there is a free memory in this heap. As soon as the memory ends (and it's allocated in the CreateVBHeaderCopy function), resources are cleared. The first DWORD of header actually stores the ID of the thread which it was created in and the FreeUnusedHeaders function checks all the headers in the heap. If a thread is completed, the memory is freed (although the ID can be repeated, but this doesn't play a special role, since in any case there will be a free memory in the heap and if the header isn't freed in one case, it will be released later). Due to the fact that the cleanup process can be run immediately from several threads, access to the cleanup is shared by the critical section tLockHeap.tWinApiSection and if some thread is already cleaning up the memory the function will return True which means that the calling thread should little bit waits and the memory will be available.

    The another feature of the module is the ability to initialize the runtime and the project and call the callback function. This can be useful for callback functions that can be called in the context of an arbitrary thread (for example, InternetStatusCallback). To do this, use the InitCurrentThreadAndCallFunction and InitCurrentThreadAndCallFunctionIDEProc functions. The first one is used in the compiled application and takes the address of the callback function that will be called after the runtime initialization, as well as the parameter to be passed to this function. The address of the first parameter is passed to the callback procedure to refer to it in the user procedure:

    Code:
    ' // This function is used in compiled form
    Public Function CallbackProc( _
                    ByVal lThreadId As Long, _
                    ByVal sKey As String, _
                    ByVal fTimeFromLastTick As Single) As Long
        ' // Init runtime and call CallBackProc_user with VarPtr(lThreadId) parameter
        InitCurrentThreadAndCallFunction AddressOf CallBackProc_user, VarPtr(lThreadId), CallbackProc
    End Function
    
    ' // Callback function is called by runtime/window proc (in IDE)
    Public Function CallBackProc_user( _
                    ByRef tParam As tCallbackParams) As Long
    
    End Function
    CallBackProc_user will be called with the initialized runtime.

    This function doesn't work in the IDE because in the IDE everything works in the main thread. For debugging in the IDE the function InitCurrentThreadAndCallFunctionIDEProc is used which returns the address of the assembler thunk that translates the call to the main thread and calls the user function in the context of the main thread. This function takes the address of the user's callback function and the size of the parameters in bytes. It always passes the address of the first parameter as a parameter of a user-defined function. I'll tell you a little more about the work of this approach in the IDE. To translate a call from the calling thread to the main thread it uses a message-only window. This window is created by calling the InitializeMessageWindow function. The first call creates a WindowProc procedure with the following code:

    Code:
        CMP DWORD [ESP+8], WM_ONCALLBACK
        JE SHORT L
        JMP DefWindowProcW
    L:  PUSH DWORD PTR SS:[ESP+10]
        CALL DWORD PTR SS:[ESP+10]
        RETN 10
    As you can see from the code, this procedure "listens" to the WM_ONCALLBACK message which contains the parameter wParam - the function address, and in the lParam parameters. Upon receiving this message it calls this procedure with this parameter, the remaining messages are ignored. This message is sent just by the assembler thunk from the caller thread. Futher, a window is created and the handle of this window and the code heap are stored into the data of the window class. This is used to avoid a memory leak in the IDE because if the window class is registered once, then these parameters can be obtained in any debugging session. The callback function is generated in InitCurrentThreadAndCallFunctionIDEProc, but first it's checked whether the same callback procedure has already been created (in order to don't create the same thunk). The thunk has the following code:

    Code:
    LEA EAX, [ESP+4]
    PUSH EAX
    PUSH pfnCallback
    PUSH WM_ONCALLBACK
    PUSH hMsgWindow
    Call SendMessageW
    RETN lParametersSize
    As you can see from the code, during calling a callback function, the call is transmitted via SendMessage to the main thread. The lParametersSize parameter is used to correctly restore the stack.

    The next feature of the module is the creation of objects in a separate thread, and you can create them as private objects (the method is based on the code of the NameBasedObjectFactory by firehacker module) as public ones. To create the project classes use the CreatePrivateObjectByNameInNewThread function and for ActiveX-public classes CreateActiveXObjectInNewThread and CreateActiveXObjectInNewThread2 ones. Before creating instances of the project classes you must first enable marshaling of these objects by calling the EnablePrivateMarshaling function. These functions accept the class identifier (ProgID / CLSID for ActiveX and the name for the project classes) and the interface identifier (IDispatch / Object is used by default). If the function is successfully called a marshaled object and an asynchronous call ID are returned. For the compiled version this is the ID of thread for IDE it's a pointer to the object. Objects are created and "live" in the ActiveXThreadProc function. The life of objects is controlled through the reference count (when it is equal to 1 it means only ActiveXThreadProc refers to the object and you can delete it and terminate the thread).
    You can call the methods either synchronously - just call the method as usual or asynchronously - using the AsynchDispMethodCall procedure. This procedure takes an asynchronous call ID, a method name, a call type, an object that receives the call notification, a notification method name and the list of parameters. The procedure copies the parameters to the temporary memory, marshals the notification object, and sends the data to the object's thread via WM_ASYNCH_CALL. It should be noted that marshaling of parameters isn't supported right now therefore it's necessary to transfer links to objects with care. If you want to marshal an object reference you should use a synchronous method to marshal the objects and then call the asynchronous method. The procedure is returned immediately. In the ActiveXThreadProc thread the data is retrieved and a synchronous call is made via MakeAsynchCall. Everything is simple, CallByName is called for the thread object and CallByName for notification. The notification method has the following prototype:

    Code:
    Public Sub CallBack (ByVal vRet As Variant)
    , where vRet accepts the return value of the method.

    The following functions are intended for marshaling: Marshal, Marshal2, UnMarshal, FreeMarshalData. The first one creates information about the marshaling (Proxy) of the interface and puts it into the stream (IStream) that is returned. It accepts the interface identifier in the pInterface parameter (IDispatch / Object by default). The UnMarshal function, on the contrary, receives a stream and creates a Proxy object based on the information in the stream. Optionally, you can release the thread object. Marshal2 does the same thing as Marshal except that it allows you to create a Proxy object many times in different threads. FreeMarshalData releases the data and the stream accordingly.
    If, for example, you want to transfer a reference to an object between two threads, it is enough to call the Marshal / UnMarshal pair in the thread which created the object and in the thread that receives the link respectively. In another case, if for example there is the one global object and you need to pass a reference to it to the multiple threads (for example, the logging object), then Marshal2 is called in the object thread, and UnMarshal with the bReleaseStream parameter is set to False is called in client threads. When the data is no longer needed, FreeMarshalData is called.

    The WaitForObjectThreadCompletion function is designed to wait for the completion of the object thread and receives the ID of the asynchronous call. It is desirable to call this function always at the end of the main process because an object thread can somehow interact with the main thread and its objects (for example, if the object thread has a marshal link to the interface of the main thread).

    The SuspendResume function is designed to suspend/resume the object's thread; bSuspend determines whether to sleep or resume the thread.

    In addition, there are also several examples in the attacment of working with module:
    1. Callback - the project demonstrates the work with the callback-function periodically called in the different threads. Also, there is an additional project of native dll (on VB6) which calls the function periodically in the different threads;
    2. JuliaSet - the Julia fractal generation in the several threads (user-defined);
    3. CopyProgress - Copy the folder in a separate thread with the progress of the copy;
    4. PublicMarshaling - Creating public objects (Dictionary) in the different threads and calling their methods (synchronously / asynchronously);
    5. PrivateMarshaling - Creating private objects in different threads and calling their methods (synchronously / asynchronously);
    6. MarshalUserInterface - Creating private objects in different threads and calling their methods (synchronously / asynchronously) based on user interfaces (contains tlb and Reg-Free manifest).


    The module is poorly tested so bugs are possible. I would be very glad to any bug-reports, wherever possible I will correct them.
    Thank you all for attention!

    Best Regards,
    The trick.
    Really a very useful example。

  3. #3
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: [VB6] - Module for working with multithreading.

    Now this is a lot of work.... It basically sums up all the correct/stable methods needed, and includes the most advanced methods available (developed by Curland, Trick, and firehacker) as far as threading in VB.
    well done.

    edit: this is top. I definitely appreciate the work that went in to creating a stable asynchronous framework (in any language). Thanks for working out all the more advance methods, and then putting it into something usable for most anyone. Just a great contribution. Thanks Trick.

    edit 2: also I was going to ask you to work out using a typelib embedded in a resource for marshaling a custom interface...
    reg-free. but you already included it (mostly). ridiculous.
    Last edited by DEXWERX; Jun 13th, 2018 at 08:12 AM.

  4. #4
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: [VB6] - Module for working with multithreading.

    have you worked out using a typelib that is compiled into the .RES as well? is the manifest able to point to an embedded typelib?
    is it as simple as modifying the xml to something like this?

    Code:
      <file name="MarshalUserInterface.exe/3">
        <typelib tlbid="{2E8B35BD-EE5B-4CB8-9EBB-132017779212}"
                 version="1.0"
                 helpdir=""/>
      </file>

  5. #5

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015
    Posts
    1,590

    Re: [VB6] - Module for working with multithreading.

    xxdoc123, DEXWERX thanks for review!

    Quote Originally Posted by DEXWERX View Post
    is the manifest able to point to an embedded typelib?
    is it as simple as modifying the xml to something like this?
    Yes. You can specify the embedded typelib too.

    Added.

    To include a typelib you need to add resource with type "typelib" and needed identifier:


    You need to specify the typelib as:
    Code:
      <file name="MarshalUserInterface.exe\3">
        <typelib tlbid="{2E8B35BD-EE5B-4CB8-9EBB-132017779212}"
                 version="1.0"
                 helpdir=""/>
      </file>
    with backslash.

  6. #6
    PowerPoster
    Join Date
    Jun 2015
    Posts
    2,224

    Re: [VB6] - Module for working with multithreading.

    Thanks! I can't think of a use case you haven't covered.


  7. #7
    New Member
    Join Date
    Mar 2016
    Posts
    15

    Re: [VB6] - Module for working with multithreading.

    Great work Trick, I appreciate the work you did

  8. #8
    PowerPoster wqweto's Avatar
    Join Date
    May 2011
    Posts
    2,079

    Re: [VB6] - Module for working with multithreading.

    This is a monumental effort, The trick!

    You have to back-link all previous efforts to this one because it took me some time to figure out which one is new and which one is obsolete.

    FYI, here is MZ Tools->Review Source Code results on modMultiThreading

    Code:
    CallbackTest
       Modules
          modMultiThreading (modMultiThreading2.bas)
             (General)-(Declarations)
                The constant 'HEAP_NO_SERIALIZE' is not used
                The constant 'ERROR_NO_MORE_ITEMS' is not used
                The constant 'WM_ONCALLBACK' is not used (it is used only inside commented block)
                The declaration Declare 'CreateIExprSrvObj2' is not used
                The declaration Declare 'VariantCopy' is not used
                The declaration Declare 'VariantClear' is not used
                The declaration Declare 'VariantCopyInd' is not used
                The declaration Declare 'GetCurrentThreadId' is not used
             Public Function EnablePrivateMarshaling( _
                The variable 'lType' is not used
                The variable 'bData' is not used
                The variable 'lSize' is not used
                The variable 'lRet' is not used
             Private Function FindThunk( _
                The variable 'lIndex' is not used
             Public Function UnMarshal( _
                The variable 'lStmSize' is not used
                The variable 'cSize' is not used
             Private Function MakeAsynchCall( _
                The variable 'pvArgs' is not used
             Private Function ThreadProc( _
                The variable 'hr' is not used
                The variable 'pContext' is not used
                The variable 'pProjInfo' is not used
                The variable 'lIsNative' is not used
             Private Sub FreeHeaderCopy( _
                The procedure 'FreeHeaderCopy' is not used
             Private Sub ModifyVBHeader( _
                The variable 'lModulesCount' is not used
    cheers,
    </wqw>

  9. #9

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015
    Posts
    1,590

    Re: [VB6] - Module for working with multithreading.

    wqweto, thank you for response! I'll update the old solutions to refer to this one.

    ADDED:

    Just i'm working on the DLL solution (based on the Standard EXE project) but i faced with the restriction when we can't control unloading. For example, if we created the several objects from the several threads and unload the DLL we can't control lifetime of the created objects from the thread which is unloading the DLL (i.e. we can't call IUnknown::Release from that thread).
    Just i'm thinking about such framework, how to make it more handful? Any ideas? Will that framework be useful? On the one hand it can be useful to understand the internal undocumented runtime's structures.

  10. #10

  11. #11
    Hyperactive Member
    Join Date
    Jul 2002
    Posts
    379

    Re: [VB6] - Module for working with multithreading.

    Just had time to study this a bit,

    GroundBreaking!!!

    Thank you Trick!

  12. #12
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,

    Only thing remaining is getting vbheader for activex exe application ( in both stand alone and activex component start modes ) so that your threading supports all types of vb6 projects.You can use VirtualQuery API as I do for getting vbheader of standard exe being used as dll in link http://www.vbforums.com/showthread.p...n-standard-dll #27

    regards,
    JSVenu

  13. #13
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,
    All your examples provided multithreading with using a copy of vbheader with modification in each thread.I understand that vb6 by default implements all these in multithreading in activex exe.The same GetVBHeader can be used for getting vbheader of activex exe.If you can include an example of activex exe multithreading which avoids clearing of global variables in the code bank then code bank will be complete in the sense it supports all types of vb6 applications(dll,exe,activex exe).

    regards,
    JSVenu

  14. #14
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,

    I understand that for each new thread in your module you use new copy of vbheader.
    Can you suggest me a way to get vbheader of a running thread in a multithreaded vb6 standard exe running application so that I can change the threading model of this particular thread based on the requirement like using user control of the same project in this thread.

    regards,
    JSVenu

  15. #15

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015
    Posts
    1,590

    Re: [VB6] - Module for working with multithreading.

    I understand that for each new thread in your module you use new copy of vbheader.
    Can you suggest me a way to get vbheader of a running thread in a multithreaded vb6 standard exe running application so that I can change the threading model of this particular thread based on the requirement like using user control of the same project in this thread.
    What's the scenario requires that? When you create a thread you specify the copy which you can change as you prefer.

  16. #16
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    What's the scenario requires that? When you create a thread you specify the copy which you can change as you prefer.
    Dear Trick,

    Thankyou for the clarification.
    But suppose we change a thread's vbheader copy to apartment model and then try to close the application the application fails to close since the exe process is still running as can be seen in task manager.
    Can you provide me code to close application properly by terminating any present hidden COM msg loop running so that it is not still running in Task manager after closing.

    regards,
    JSVenu

  17. #17

  18. #18
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    Provide the StdEXE example which is still running after closing using current module.
    Dear Trick,

    Here is the attachment.

    Here I am using only main thread and making it to apt model thru its vbheader.

    When we start the form and click on set to apt button the main thread is changed to apt model.
    Then if we close the form the app still runs as can be seen in task manager.

    How to deal with this problem when we have more than one thread like we create new threads with their vbheader set to apartment model using your vbcreatethread module function.

    Please clarify.

    regards,
    JSVenu
    Attached Files Attached Files
    Last edited by jsvenu; Feb 28th, 2020 at 03:00 AM.

  19. #19

  20. #20
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    You shouldn't change the vbHeader of main thread.
    Dear Trick,

    Thankyou for the reply.I tried and the application works fine when main thread vbheader is not changed and using vbcreatethread api for additional threads with their vbheader copy changed to apt model.
    I have also tested the same by making the same application run as activex exe in stand alone mode by changing the project settings to thread per object. Even this activex exe worked perfect with vbheader and your vbcreatethread module function.
    So I can say that any activex exe application can also use vbheader and vbcreatethread (thru createthread api) for multithreading.

    So Trick my doubt is why a activex exe application works for vbheader and createthread api but not with vbheader and CreateObject(for creating thread thru multiuse class).Even when I try to use Createvbheadercopy module function in CreateObject thread it gives error.
    What is the difference between thread creating in activex exe thru CreateThread and CreateObject.
    As you told we can maintain a copy of vbheader in standard exe as well as activex exe as long as we use vbcreatethread instead of CreateObject(for actx exe).
    Is there any other way to change threading model of activex exe other than using vbheader when we use CreateObject instead of vbcreatethread api to make this support usercontrol in new thread.Please clarify.

    regards,
    JSVenu

  21. #21

  22. #22
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    I didn't research the details of ActiveX EXE. If you want to use an usercontrol in an ActiveX EXE project Olaf gave the very good example. If you use the StdEXE approach you can use the solution which i gave. Right now i'm working on the new InlineAssembler Add-in and have no time to research the details of AxEXE.
    Dear Trick,
    OK. Thankyou.
    regards,
    JSVenu

  23. #23
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,



    The code attached contains a standard exe project (ThreadClient) in which activex dll (FormCreator) is loaded.


    Code:
    'Standard exe project ThreadClient 
    
    'frmMain code
    
    Option Explicit
    
    Dim mcCreator   As FormCreator.CFormCreator
    
    
    Private Sub Command4_Click()
     mcCreator.init (App.hInstance)
     mcCreator.createforminthread ("Form1")
    End Sub
    
    Private Sub Form_Load()
        Me.Caption = App.ThreadID
        Set mcCreator = New CFormCreator
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
      
        Set mcCreator = Nothing
        
    End Sub


    Code:
     'Activex dll project FormCreator 
    
    
    'CFormCreator code
    
    
    
    
    Option Explicit
    
    
    Public Sub init(hinstance As Long)
        modMultiThreading.Initialize (hinstance)
    End Sub
    
    Private Sub Class_Terminate()
    
        modMultiThreading.Uninitialize
    
    End Sub
    
    Public Sub createforminthread(ByVal str As String)
    
        Set frmexe = modMultiThreading.CreatePrivateObjectByNameInNewThread(str, , lId)
    
        frmexe.Show
    
    End Sub

    When I try to use modmultithreading module to display a new form object of Form1 of standard exe in new thread of activex dll using

    modMultiThreading.CreatePrivateObjectByNameInNewThread function it crashes.Please clarify.


    regards,

    JSVenu
    Attached Files Attached Files

  24. #24
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by jsvenu View Post
    Dear Trick,



    The code attached contains a standard exe project (ThreadClient) in which activex dll (FormCreator) is loaded.


    Code:
    'Standard exe project ThreadClient 
    
    'frmMain code
    
    Option Explicit
    
    Dim mcCreator   As FormCreator.CFormCreator
    
    
    Private Sub Command4_Click()
     mcCreator.init (App.hInstance)
     mcCreator.createforminthread ("Form1")
    End Sub
    
    Private Sub Form_Load()
        Me.Caption = App.ThreadID
        Set mcCreator = New CFormCreator
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
      
        Set mcCreator = Nothing
        
    End Sub


    Code:
     'Activex dll project FormCreator 
    
    
    'CFormCreator code
    
    
    
    
    Option Explicit
    
    
    Public Sub init(hinstance As Long)
        modMultiThreading.Initialize (hinstance)
    End Sub
    
    Private Sub Class_Terminate()
    
        modMultiThreading.Uninitialize
    
    End Sub
    
    Public Sub createforminthread(ByVal str As String)
    
        Set frmexe = modMultiThreading.CreatePrivateObjectByNameInNewThread(str, , lId)
    
        frmexe.Show
    
    End Sub

    When I try to use modmultithreading module to display a new form object of Form1 of standard exe in new thread of activex dll using

    modMultiThreading.CreatePrivateObjectByNameInNewThread function it crashes.Please clarify.


    regards,

    JSVenu


    Dear Trick,

    I identified the main problem for the above ExeFormCreator project http://www.vbforums.com/attachment.p...1&d=1583397929 when I did some debugging .

    The main problem is the ThreadClient standard exe application was crashing when CreateVBHeaderCopy module function was called in new thread in FormCreator activex dll when CreatePrivateObjectByNameInNewThread is called.

    Here basically I am trying to do all the multithreading by using your modmultithreading module in activex dll and loading the same through project refereces to a standard exe.The standard exe passes its application instance App.hInstance to the activex dll through a class function parameter.Complete thread initialization and uninitialisation is done and calling CreatePrivateObjectByNameInNewThread is done in activex dll.

    Can you clarify how to make CreateVBHeaderCopy to work in activex dll perfectly just like it is working perfect in standard exe.

    regards,
    JSVenu
    Last edited by jsvenu; Mar 8th, 2020 at 08:51 AM.

  25. #25
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,

    The ExeFormCreator project http://www.vbforums.com/attachment.p...1&d=1583397929 works perfect when the FormCreator activex dll project Threading model is set to Single Threaded.

    Please clarify how to make it work without crash when the FormCreator activex dll project Threading model is set to Apartment Threaded.

    regards,
    JSVenu

  26. #26
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by jsvenu View Post
    Dear Trick,

    The ExeFormCreator project http://www.vbforums.com/attachment.p...1&d=1583397929 works perfect when the FormCreator activex dll project Threading model is set to Single Threaded.

    Please clarify how to make it work without crash when the FormCreator activex dll project Threading model is set to Apartment Threaded.

    regards,
    JSVenu
    Dear Trick,

    Any vb6 standard exe runs in single threaded mode by default.Apartment threaded mode is possible only thru vbheader modification.
    But any activex dll can run in apartment threaded mode also by default.
    Even any activex exe can run in apartment threaded mode thru its Thread per object mode by default.
    Is there any way to extract vbheader of activex dll or activex exe for a particular thread to make it apartment threaded thru vbheader modification.

    Can the above be used to make ExeFormCreator work perfect even when the FormCreator activex dll is made Apartment Threaded thru vbheader modification.Please clarify how to make ExeFormCreator work perfect without crashing even when the FormCreator activex dll is made Apartment Threaded thru vbheader modification.

    regards,
    JSVenu
    Last edited by jsvenu; Mar 27th, 2020 at 01:31 AM.

  27. #27

    Thread Starter
    Frenzied Member
    Join Date
    Feb 2015
    Posts
    1,590

    Re: [VB6] - Module for working with multithreading.

    jsvenu
    When you compile an executable file with apartment/single threading model it compiles the different code. So you can't use single threaded code like apartment. I don't understand your end goal. This module is intended to use threading in StandardEXEs ONLY.
    The ActiveX-DLL mode already has ability to use threading, the same with ActiveX-EXE.

  28. #28
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    jsvenu
    When you compile an executable file with apartment/single threading model it compiles the different code. So you can't use single threaded code like apartment. I don't understand your end goal. This module is intended to use threading in StandardEXEs ONLY.
    The ActiveX-DLL mode already has ability to use threading, the same with ActiveX-EXE.
    Dear Trick,

    I know that ActiveX-DLL already has ability to use threading.The same I did in ExeFormCreator project in FormCreator
    ActiveX-DLL but in single threaded mode for creating a form object of standard exe (ThreadSingle project) in FormCreator Activex-DLL project set to Single Threaded threading Model.

    My end goal is to achieve the same thing using FormCreator Activex-DLL project set to Apartment Threaded threading Model.

    Same is the case with http://www.vbforums.com/attachment.p...7&d=1578834223
    when I try to change the CallbackThread standard dll to activex dll but it is working perfect when CallbackThread activex dll is set to single threaded and works partially when I set to it to Apartment threaded.


    regards,
    JSVenu
    Last edited by jsvenu; Mar 27th, 2020 at 03:09 AM.

  29. #29
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by jsvenu View Post
    Dear Trick,

    I know that ActiveX-DLL already has ability to use threading.The same I did in ExeFormCreator project in FormCreator
    ActiveX-DLL but in single threaded mode for creating a form object of standard exe (ThreadSingle project) in FormCreator Activex-DLL project set to Single Threaded threading Model.

    My end goal is to achieve the same thing using FormCreator Activex-DLL project set to Apartment Threaded threading Model.

    Same is the case with http://www.vbforums.com/attachment.p...7&d=1578834223
    when I try to change the CallbackThread standard dll to activex dll but it is working perfect when CallbackThread activex dll is set to single threaded and works partially when I set to it to Apartment threaded.


    regards,
    JSVenu
    Dear Trick,

    Can you tell me the differences between CreateObject of multiuse class in activex exe with thread per object and starting a thread using CreateThread.
    One difference I know is for starting an activex exe thread using CreateObject we have to make async call.
    But for CreateThread we can make sync call in threadproc since it is free threading.
    As far as I know both call vbdllgetclassobject with vbheader for runtime init.Here I don't have access to particular thread vbheader of activex exe but I have for CreateThread.
    Can you show actually what happens out of the box when a thread is created using CreateObject for activex exe so that the same can be useful to me in understanding activex dll along with std exe better for solving the above problem.


    regards,
    JSVenu

  30. #30
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Dear Trick,

    When a thread is created using CreateThread API in activex dll called from standard exe for initializing runtime in this new thread we use a multiuse class of the activex dll using Coinitialize followed by CreateObject.
    The cycle is as follows for CreateObject used with multiuse class for runtime initialization:

    CreateObject calls CoCreateInstance.
    CoCreateInstance calls CoGetClassObject.
    CoGetClassObject calls DllGetClassObject in the dll file.
    DllGetClassObject in our DLL calls VBDllGetClassObject out of runtime.
    VBDllGetClassObject in random calls CThreadPool::InitDllAccess.
    CThreadPool::InitDllAccess calls CVBThreadAction::InitDllThrd.

    So when we call CreateObject with multiuse class indirectly VBDllGetClassObject is called which we know that takes vbheader as the parameter.

    So when we call CreateObject indirectly VBDllGetClassObject is called which we know that takes vbheader as the parameter and the application works perfect out of the box because the runtime is initialized properly.

    Now when I use vbheader along with VBDllGetClassObject instead of multiuse class with CreateObject in Activex dll with Threading Model set to Single Threaded it works perfect.But it fails when I set to Apartment Threaded Threading Model because the runtime initialization fails.

    So my doubt is when I try to use vbheader along with VBDllGetClassObject instead of multiuse class in activex dll
    why the activex dll crashes in Apartment model without initializing runtime when it is supposed to work according to the above cycle.I do not want to use multiuse class for the runtime initialization in new thread. Please clarify.

    regards,
    JSVenu

  31. #31

  32. #32
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    What's the relation to the multithreading module? Please attach the small project which shows the error.
    When you use an ActiveX-DLL/ActiveX-EXE you shouldn't use VBDllGetClassObject you just initialize STA (through CoInitialize) and create an instance (New/CoCreateInstance/etc.)
    Dear Trick,
    Here is the link to example which you already gave as example in standard dll for CallbackThread in Release.zip project.
    http://www.vbforums.com/attachment.p...7&d=1578834223


    When I try to change the CallbackThread standard dll to activex dll it is working perfect when CallbackThread activex dll is set to single threaded and works partially when I set to it to Apartment threaded.

    I could have solved it using default multiuse class of activex dll but I wanted to solve it for apartment threaded activex dll without using multiuse class for runtime init of the thread in activex dll.This already I asked in #28 earlier.

    I wanted to use vbdllgetclassobject since as I already said in the cycle CreatObject calls vbdllgetclassobject and it is supposed to work as per the explanation.Here you are using DllMain for runtime init in CallbackThread dll project when DLL_THREAD_ATTACH is called using vbdllgetclassobject with vbheader.

    regards,
    JSVenu

  33. #33

  34. #34
    Hyperactive Member
    Join Date
    Apr 2015
    Posts
    335

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    You shouldn't use modDllInitialize.bas to project types except Standard-EXE one. The ActiveX-DLLs already are DLLs. This project isn't related to current topic so please if you want to discuss about the Native-DLL project either use the old topic or create a new one. I won't answer about that and other non-related question here anymore.
    Dear Trick,
    ok I started new thread

    http://www.vbforums.com/showthread.p...69#post5463369
    regards,
    JSVenu

  35. #35
    Addicted Member
    Join Date
    Jan 2015
    Posts
    224

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    xxdoc123, DEXWERX thanks for review!


    Yes. You can specify the embedded typelib too.

    Added.

    To include a typelib you need to add resource with type "typelib" and needed identifier:


    You need to specify the typelib as:
    Code:
      <file name="MarshalUserInterface.exe\3">
        <typelib tlbid="{2E8B35BD-EE5B-4CB8-9EBB-132017779212}"
                 version="1.0"
                 helpdir=""/>
      </file>
    with backslash.
    hello the trick:
    i m curious about can we using embed com dll or ocx files in embed manifest?
    i have tried ocx files but failed.

    In Res:
    24 'typeNameId
    1 'manifest
    10 'both "10" and "CUSTOM" failed
    3 'ocx file id

    and manifest content as follows.
    <file name="TestRes.exe\3">
    <typelib tlbid="{2E8B35BD-EE5B-4CB8-9EBB-132017779212}"
    version="1.0"
    helpdir=""/>
    </file>
    Last edited by loquat; May 2nd, 2020 at 11:06 PM.

  36. #36

  37. #37
    Addicted Member
    Join Date
    Jan 2015
    Posts
    224

    Re: [VB6] - Module for working with multithreading.

    Quote Originally Posted by The trick View Post
    Hello loquat,
    you can't embed a dll file inside a manifest but you can use an embedded typelibrary inside DLL/EXE.
    ok, good to here that, save me many time, ^_^

  38. #38
    New Member
    Join Date
    May 2020
    Posts
    5

    Re: [VB6] - Module for working with multithreading.

    I find that when I link a COM reg-free manifest resource file to my exe, the multi module dosen't work. Could you please solve this problem?
    Last edited by lowe517; May 28th, 2020 at 08:16 AM.

  39. #39

  40. #40
    New Member
    Join Date
    May 2020
    Posts
    5

    Re: [VB6] - Module for working with multithreading.


Page 1 of 2 12 LastLast

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width