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

Thread: how to call a vb6 standalone exe module function from c/c++ new thread-reg

  1. #1

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    I am working on a win32 c/c++ standard dll which calls a vb6 callback module function whose address is passed as parameter in new thread through its exported function.I have source code of this dll.
    The vb6 callback module function is in standard exe.I do not have source code of this vb6 standard exe in which the above win32 standard dll is loaded.
    When the vb6 standard exe tries to call the exported function of c/c++ dll by passing the address of the callback function as parameter the application crashes.
    So I wanted to know is there any way to avoid this crash by modifying the following c/c++ dll source code.

    Code:
    c/c++ dll source code:
    
    XYZ.dll c/c++ :
    long cbAddrAsyncMod;
    void _asyncth(void * data)
    {
    typedef void (__stdcall *FUNCPTR)();
    FUNCPTR vbFunc;
    vb6Func = (FUNCPTR)cbAddrAsyncMod;
    vb6Func();
    }
    extern "C" __declspec(dllexport) void async(long addr)
    {
    cbAddrAsync = addr;
    HANDLE hHandle = (HANDLE)_beginthread(_asyncth,0, NULL);//CreateThread and _beginthreadEx also failed
    }
    when i click on vb6 standard exe GUI button , the crash occurs in c/c++ dll

    Thanks

  2. #2

  3. #3

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by The trick View Post
    You should route the call to main STA.
    Can you show me how to do it in the dll code.

  4. #4
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Create an object in main STA and marshal the reference to the new thread using CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream pair but you need to initialize/support STA in new thread. The most simplier way i think is use windows messages (SendMessage). You can see this approach here. The calls from different threads are routed to the STA thread.
    https://www.vbforums.com/showthread....=1#post5551526

  5. #5

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    I understood that dll async exported callback function is main thread assuming it is called from vb6 std exe.
    I have to use either of two methods shown:

    1. Create an object in main STA and marshal the reference to the new thread using CoMarshalInterThreadInterfaceInStream/CoGetInterfaceAndReleaseStream pair but you need to initialize/support STA in new thread.
    Can you provide me a simple code to achieve this in the dll code.

    2.use windows messages (SendMessage). The calls from different threads are routed to the STA thread.I will go thru the given link and try and let you know.


    Thanks

  6. #6

  7. #7

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    I have gone thru the QPCTimer , VbVst-main.Both use activex dll in single threaded mode just like our standard dll c/c++ code.
    Both route the new thread to the main STA thread.

    I modified the c/c++ dll code as follows:

    Code:
    long cbAddrAsyncMod;
    	HANDLE hth;
    	DWORD dw;
    	 typedef int (__stdcall *FUNCPTR)();
    	FUNCPTR vbFunc;
    ULONG WINAPI  _asynth(void* data)
    {
       HRESULT  hr; 
       vbFunc = (FUNCPTR)cbAddrAsyncMod;
       hr = CoInitialize(NULL);
       PostThreadMessage(dw,WM_USER+1,0,0);
       return 0;
    }
    
    extern "C" __declspec(dllexport) void async(long addr)
    {
        cbAddrAsync = addr;
    	MSG msg;
    	DWORD dw1;
    	dw=GetCurrentThreadId();
    	hth = (HANDLE) CreateThread(0,0,_asynth,0,0,&dw1);
    	while (GetMessage(&msg,0,0,0))
    	{
    		if (msg.message==WM_USER+1) vbFunc();
    		else
    		{
    		TranslateMessage(&msg);
    		DispatchMessage(&msg);
    		}
    	}
    }
    This solves half of the problem:

    1. The crash is avoided.

    But private objects of vb6 std exe are created in main thread instead of new thread which can be checked using GetCurrentThreaID() API in the dll code.

    Now the left over part of the problem is

    2. How to make the private objects of vb6 std exe created in new thread of dll again by modifying the c/c++ dll code.

    Thanks.

  8. #8
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    But private objects of vb6 std exe are created in main thread instead of new thread which can be checked using GetCurrentThreaID() API in the dll code.
    A vb6 std-exe is STA-based one so all the objects live only in a single thread.

    How to make the private objects of vb6 std exe created in new thread of dll again by modifying the c/c++ dll code.
    If you want to break the rules. See this module again. This allows to create private objects in new threads.

  9. #9

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by The trick View Post
    A vb6 std-exe is STA-based one so all the objects live only in a single thread.


    If you want to break the rules. See this module again. This allows to create private objects in new threads.
    Hello The Trick,

    I want to create the same private objects in my new c/c++ dll thread without having to modify vb6 std exe since in the beginning itself I mentioned this.

    Thanks

  10. #10
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by smkperu View Post
    I want to create the same private objects in my new c/c++ dll ...
    If you don't know how to do that in C++: ...ask in a C++ forum!

    If you do know how to do that in C++: ...what do you need VB6 for?

    It's becoming ridiculous.

    Olaf

  11. #11
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by Schmidt View Post
    If you don't know how to do that in C++: ...ask in a C++ forum!

    If you do know how to do that in C++: ...what do you need VB6 for?

    It's becoming ridiculous.

    Olaf
    I barely understand any of the wizardry in this thread but what little I do get tells me this is more of a COM thing than a C++ thing. VB6 programmers tend towards being COM experts if nothing else

    My impression is that OP is just way over his head.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  12. #12

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    The following is the vb6 PrivateObjectCreatorofVB6Stdexe activex dll Project in single threaded mode CPrivateObjectCreatorofVB6Stdexe class code with CreatePrivateObjectByNameInNewThread() member function for which the private object is passed as String from exe:

    CPrivateObjectCreatorofVB6Stdexe.cls code

    Code:
    Option Explicit
    Private Sub Class_Terminate()
        modMultiThreading.Uninitialize
    End Sub
    
    Public Sub createPrivateObjectinthread(ByVal str As String)
        modMultiThreading.Initialize() 'initializes VB6Stdexe project using GetModuleHandle(0) for getting exe  instance
        Set frmexe = modMultiThreading.CreatePrivateObjectByNameInNewThread(str, , lId) 'exists in modMultiThreading2.bas
        frmexe.Show
    End Sub

    You can take any simple vb6 stdexe and pass any of its private object say "Form1" as follows:



    frmMain.frm code

    Code:
    Option Explicit
    
    Dim mcCreator   As PrivateObjectCreatorofVB6Stdexe.CPrivateObjectCreatorofVB6Stdexe
    
    
    Private Sub Command1_Click()
     mcCreator.createPrivateObjectinthread("Form1")
    End Sub
    
    Private Sub Form_Load()
        Set mcCreator = New CPrivateObjectCreatorofVB6Stdexe
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
      
        Set mcCreator = Nothing
        
    End Sub
    Just we have to add PrivateObjectCreatorofVB6Stdexe.dll to the vb6 stdexe thru project, references ,build and run the vb6 std exe in compiled mode.

    If we don't want to use CPrivateObjectCreatorofVB6Stdexe in activex dll and and want only Exported function like CreatePrivateObjectByNameInNewThread, we can do that by modifying the LINKER SWITCHES and adding some initialization code in DLLMain().This exported function can be called in any vb6 std exe after declaring it in using Declare statement.

    So I request you for the same PrivateObjectCreatorofVB6Stdexe.dll code in c/c++ which can work with any vb6 std exe.

    Thanks.

  13. #13

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    In order to run a module callback function from vb6 std exe from my c/c++ dll using your multithreading module in vb6 activex dll I made three projects:

    1. Threadclient vb6 std exe ( which calls exported function of c/c++ win32 std dll)

    2. myvcdll c/c++ win32 standard dll (which calls vbcallback vb6 activex dll)

    3 vbcallback activex dll in (single threaded mode)


    Details:


    I made a simple activex dll (single threaded mode) vbcallback.dll containing Class1 multiuse class as follows using your modMultiThreading2.bas:


    Class1.cls code

    Code:
    Option Explicit
    
    Private Sub Class_Initialize()
     modMultiThreading.Initialize 0
    End Sub
    
    Private Sub Class_Terminate()
        modMultiThreading.Uninitialize
    End Sub
    
    
    Public Function InitCurrentThreadAndCallFunction( _
                    ByVal pfnCallback As Long, _
                    ByVal pParam As Long, _
                    ByRef lReturnValue As Long) As Boolean
    
       modMultiThreading.InitCurrentThreadAndCallFunction _
                    pfnCallback, _
                    pParam, _
                    lReturnValue
    End Function
    
    Public Function InitCurrentThreadAndCallFunctionIDEProc( _
                    ByVal pfnCallback As Long, _
                    ByVal lParametersSize As Long) As Long
       
        
        modMultiThreading.InitCurrentThreadAndCallFunctionIDEProc pfnCallback, _
                    lParametersSize
        
    End Function
    I made a standard win32 dll in c/c++ myvcdll.dll which

    1.Exports fnMyvcdll function accepting module function pointer from any simple vb6 std exe
    2. imports the above vbcallback.dll using #import which generated the .tli and tlh files.
    Then I included the .tlh file using #include and called in a new thread as follows:

    myvcdll.cpp code

    Code:
     
         _Class1Ptr cobj;//smart pointer    
         int ( __stdcall *lpVBFunction ) (int,int);
         long glpfnTest;
    	  DWORD WINAPI justDoIt(LPVOID lpParameter)
          {
    		 
    		  	  long retVal;
    		  char tmp[50];
    		  HRESULT hr11;
    		  cobj->InitCurrentThreadAndCallFunction(glpfnTest,0,&retVal);//crashes at this line
    
    		  sprintf(tmp, "Return Val from CallBack was %d", retVal);
    
    		  MessageBox(0,tmp,"VB Callback Result",0);
    		  return 0;
    	  }
    
    // This is an example of an exported function.
    MYVCDLL_API int fnMyvcdll(long lpfnTest)
    {
    	 	 HANDLE hThrd;
    
               DWORD tid;
    		   glpfnTest=lpfnTest;
    		
    
    
             if((hThrd = CreateThread(
                       0,
                       0,
                       (LPTHREAD_START_ROUTINE)justDoIt,0,
                       0,
                       &tid)) == NULL)
               {
                  //error handling here
               }
               CloseHandle(hThrd);
        
    	
    	return 0;
    }
    In vb6 standard exe ThreadClient I called fnMyvcdll exported function by passing module function add as follows:


    frmMain.frm code:

    Code:
    Option Explicit
    
    Private Declare Function fnMyvcdll Lib "myvcdll.dll" _
                (ByVal addr As Long) As Integer
    
    
    
    Private Sub Command1_Click()
    fnMyvcdll AddressOf add
    End Sub
    Module1.bas code:

    Code:
    Public Function add(ByVal a As Integer, ByVal b As Integer) As Long
    MsgBox "in add"
    add = a + b
    End Function
    But when I click on form button1 in vb6 std exe ThreadClient it crashes at the line

    cobj->InitCurrentThreadAndCallFunction(glpfnTest,0,&retVal); in myvcdll.dll

    Can you explain me how to avoid the crash and make my c/c++ dll call the callback of vb6 std exe
    in new thread using vb6 activex dll.

    Code attached without binaries.


    Thanks
    Attached Files Attached Files

  14. #14

  15. #15

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by The trick View Post
    I don't see the object creation. cobj is a smart pointer to VB object. Where do you create this object?
    Hello The Trick,

    OK.I created the object as follows in myvcdll.cpp in myvcdll project:

    Code:
    DWORD WINAPI justDoIt(LPVOID lpParameter)
          {
    		 
    		  	  long retVal;
    		  char tmp[50];
    		  HRESULT hr11;
    
    
        		      cobj.CreateInstance(__uuidof(_Class1));//added
    			  cobj->InitCurrentThreadAndCallFunction(glpfnTest,0,&retVal);
    
    		  sprintf(tmp, "Return Val from CallBack was %d", retVal);
    
    		  MessageBox(0,tmp,"VB Callback Result",0);
    		  return 0;
    	  }
    myvcdll project attached
    Still I get the crash.

    Thanks
    Attached Files Attached Files

  16. #16

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    The main problem is I am getting cobj smart pointer as NULL even though when I use
    cobj.CreateInstance(__uuidof(_Class1));//cobj is NULL

    But when I use the following code I get valid IDispatch pointer.



    Code:
    	 CLSID      clsid;
    	 HRESULT	   hr;
    	
    	
         LPOLESTR   p = OLESTR("vbcallback.Class1");
    	
    	
         hr = CoInitialize(NULL);
    
    	
    	 hr = CLSIDFromProgID( p , &clsid);
    
    	
    
    		 hr =  CoCreateInstance( clsid,
    							 NULL,
    							 CLSCTX_INPROC_SERVER,
    							 IID_IDispatch  ,
    							 (void**) &IDisp);//success
    If I can get cobj from this valid IDisp I can run
    cobj->InitCurrentThreadAndCallFunction(glpfnTest,0,&retVal);
    How to get cobj from this valid IDisp
    Thanks

  17. #17

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Dear Trick,

    I got cobj from IDisp succesfully as follows:

    Code:
    	 CLSID      clsid;
    	 HRESULT	   hr;
    	
    	
         LPOLESTR   p = OLESTR("vbcallback.Class1");
    	
    	
         hr = CoInitialize(NULL);
    
    	
    	 hr = CLSIDFromProgID( p , &clsid);
    
    	
    
    		 hr =  CoCreateInstance( clsid,
    							 NULL,
    							 CLSCTX_INPROC_SERVER,
    							 IID_IDispatch  ,
    							 (void**) &IDisp);//success
    
    CComPtr<IDispatch> spDisp(IDisp);
    
          cobj=spDisp;//success
    But when I call cobj->InitCurrentThreadAndCallFunction(glpfnTest,0,&retVal);


    it crashes.

    Updated myvcdll c/c++ dll project attached.

    Thanks
    Attached Files Attached Files

  18. #18

  19. #19

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by The trick View Post
    You can't use this module for AxDll. If you need to callback of a function you could just use an object in the main (exe) thread. Just pass this object reference to another thread using marshaling. The callback will be in the main thread anywhere.
    Can you show me an example code of using this between a simple vb6 standard exe module callback and c/c++ dll calling this vb6 callback in new thread

    Thanks.

  20. #20
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Code:
    #include <Windows.h>
    #include <shlwapi.h>
    
    #pragma comment (lib, "Shlwapi.lib")
    #pragma comment (linker, "/EXPORT:CreateThreadAndCallback=_CreateThreadAndCallback@12")
    
    class CCallbackObj : public IDispatch {
    	
    private:
    
    	ULONG m_uRefCtr;
    
    public:
    
    	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void  **ppvObject) {
    
    		if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch)) {
    			*ppvObject = (void*)this;
    		} else {
    			return E_NOINTERFACE;
    		}
    
    		return S_OK;
    
    	}
    
        ULONG STDMETHODCALLTYPE AddRef() {
    		return m_uRefCtr++;
    	}
    
        ULONG STDMETHODCALLTYPE Release() {
    		return m_uRefCtr--;
    	}
    
        HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
    		HRESULT hr = S_OK;
    
    		for (UINT i = 0; i < cNames; i++) {
    			if (lstrcmpW(rgszNames[i], L"CallFunc") == 0) {
    				rgDispId[i] = 1;
    			} else {
    				rgDispId[i] = DISPID_UNKNOWN;
    				hr = DISPID_UNKNOWN;
    			}
    		}
    
    		return hr;
    	}
            
        HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
                 						 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
    
    		if (!IsEqualGUID(GUID_NULL, riid))
    			return DISP_E_UNKNOWNNAME;
    
    		if (wFlags & DISPATCH_METHOD) {
    
    			if (!pDispParams)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (!pDispParams->rgvarg)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (pDispParams->cArgs != 3)
    				return DISP_E_BADPARAMCOUNT;
    
    			if (dispIdMember != 1)
    				return DISP_E_MEMBERNOTFOUND;
    
    			if (pDispParams->rgvarg->vt != VT_I4)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[1].vt != VT_I4 && pDispParams->rgvarg[1].vt != VT_I2 && pDispParams->rgvarg[1].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[2].vt != VT_I4 && pDispParams->rgvarg[2].vt != VT_I2 && pDispParams->rgvarg[2].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			LONG (__stdcall *pfn)(LONG, LONG) = (LONG(__stdcall *)(LONG, LONG))pDispParams->rgvarg->lVal;
    			LONG lResult = pfn(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[2].lVal);
    
    			if (pVarResult) {
    				pVarResult->vt = VT_I4;
    				pVarResult->lVal = lResult;
    			}
    
    		} else {
    			return DISP_E_MEMBERNOTFOUND;
    		}
    
    		return S_OK;
    	}
    
    };
    
    struct TThreadData {
    	IStream *pStm;
    	IDispatch *pObj;
    	LPVOID pfn;
    	LONG l1;
    	LONG l2;
    };
    
    CCallbackObj m_cObj;
    
    BOOL WINAPI DllMain(HINSTANCE, DWORD,LPVOID){
    	return TRUE;
    }
    
    ULONG WINAPI ThreadProc(TThreadData *pData) {
    	DISPPARAMS tParams;
    	VARIANTARG tVarArgs[3];
    	VARIANT tVarResult = {VT_EMPTY};
    
    	if (pData->pObj) {
    
    		tParams.cArgs = 3;
    		tParams.cNamedArgs = 0;
    		tParams.rgdispidNamedArgs = NULL;
    		tParams.rgvarg = tVarArgs;
    
    		tVarArgs[0].vt = VT_I4; tVarArgs[0].lVal = (LONG)pData->pfn;
    		tVarArgs[1].vt = VT_I4; tVarArgs[1].lVal = pData->l1;
    		tVarArgs[2].vt = VT_I4; tVarArgs[2].lVal = pData->l2;
    
    		pData->pObj->Invoke(1, GUID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &tParams, &tVarResult, NULL, NULL);
    		pData->pObj->Release();
    
    	}
    
    	GlobalFree(pData);
    
    	return 0;
    
    }
    
    ULONG WINAPI Unmarshal(TThreadData *pData) {
    
    	HRESULT hr;
    	hr = CoGetInterfaceAndReleaseStream(pData->pStm, IID_IDispatch, (void**)&pData->pObj);
    	pData->pStm = NULL;
    
    	return hr;
    
    }
    
    extern "C" HRESULT WINAPI CreateThreadAndCallback(LPVOID pfn, LONG l1, LONG l2) {
    	HRESULT hr = S_OK;
    	TThreadData *pData = (TThreadData*)GlobalAlloc(GMEM_ZEROINIT, sizeof(TThreadData));
    
    	if (!pData)
    		return E_OUTOFMEMORY;
    
    	pData->pfn = pfn;
    	pData->l1 = l1;
    	pData->l2 = l2;
    
    	if (FAILED(hr = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, &m_cObj, &pData->pStm))) {
    		GlobalFree(pData);
    		return hr;
    	}
    
    	if (!SHCreateThread((LPTHREAD_START_ROUTINE)ThreadProc, pData, CTF_COINIT_STA, (LPTHREAD_START_ROUTINE)Unmarshal)) {
    		pData->pStm->Release();
    		GlobalFree(pData);
    		hr = E_FAIL;
    	}
    
    	return hr;
    
    }
    Code:
    Option Explicit
    
    Private Declare Function CreateThreadAndCallback Lib "dll" ( _
                             ByVal pfn As Long, _
                             ByVal l1 As Long, _
                             ByVal l2 As Long) As Long
    
    Private Sub Command1_Click()
        CreateThreadAndCallback AddressOf Callback, 123, 123000
    End Sub
    Code:
    Option Explicit
    
    Public Function Callback( _
                    ByVal l1 As Long, _
                    ByVal l2 As Long) As Long
        Callback = l1 + l2
        MsgBox Callback
    End Function
    Attached Files Attached Files

  21. #21

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    Thankyou for the example.

    1. When I include your main.cpp code in a win32 dll and try to compile the dll I get the following compilation errors.

    ...\cdll.cpp(202) : error C2065: 'SHCreateThread' : undeclared identifier
    ...\cdll.cpp(202) : error C2065: 'CTF_COINIT_STA' : undeclared identifier

    How to avoid this errors


    2. Is there any way to use CreateThread instead of SHCreateThread since I never used SHCreateThread() and I am more familiar with simple CreateThread.If it is really required then OK.


    Code attached

    Thanks
    Attached Files Attached Files

  22. #22

  23. #23

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    I modified the application as follows:



    Code:
    #include "stdafx.h"
    
    #include <Windows.h>
    #include <shlwapi.h>
    #include <ole2.h>
    #include <objbase.h>
    //#include <comdef.h>
    #include <atlbase.h>
    
    #pragma comment (lib, "Shlwapi.lib")
    #pragma comment (linker, "/EXPORT:CreateThreadAndCallback=_CreateThreadAndCallback@12")
    #define CTF_COINIT_STA  0x00000008
    typedef BOOL (tSHCreateThread)( LPTHREAD_START_ROUTINE , VOID *,  DWORD,  LPTHREAD_START_ROUTINE );
    
    
    BOOL (*fSHCreateThread)( LPTHREAD_START_ROUTINE , VOID *,  DWORD,  LPTHREAD_START_ROUTINE );
    HMODULE hMod; 
     
     
     
    class CCallbackObj : public IDispatch {
    	
    private:
    
    	ULONG m_uRefCtr;
    
    public:
    
    	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void  **ppvObject) {
    
    		if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch)) {
    			*ppvObject = (void*)this;
    		} else {
    			return E_NOINTERFACE;
    		}
    
    		return S_OK;
    
    	}
    
        ULONG STDMETHODCALLTYPE AddRef() {
    		return m_uRefCtr++;
    	}
    
        ULONG STDMETHODCALLTYPE Release() {
    		return m_uRefCtr--;
    	}
    
        HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
    		HRESULT hr = S_OK;
    
    		for (UINT i = 0; i < cNames; i++) {
    			if (lstrcmpW(rgszNames[i], L"CallFunc") == 0) {
    				rgDispId[i] = 1;
    			} else {
    				rgDispId[i] = DISPID_UNKNOWN;
    				hr = DISPID_UNKNOWN;
    			}
    		}
    
    		return hr;
    	}
            
        HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
                 						 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
    
    		if (!IsEqualGUID(GUID_NULL, riid))
    			return DISP_E_UNKNOWNNAME;
    
    		if (wFlags & DISPATCH_METHOD) {
    
    			if (!pDispParams)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (!pDispParams->rgvarg)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (pDispParams->cArgs != 3)
    				return DISP_E_BADPARAMCOUNT;
    
    			if (dispIdMember != 1)
    				return DISP_E_MEMBERNOTFOUND;
    
    			if (pDispParams->rgvarg->vt != VT_I4)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[1].vt != VT_I4 && pDispParams->rgvarg[1].vt != VT_I2 && pDispParams->rgvarg[1].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[2].vt != VT_I4 && pDispParams->rgvarg[2].vt != VT_I2 && pDispParams->rgvarg[2].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			LONG (__stdcall *pfn)(LONG, LONG) = (LONG(__stdcall *)(LONG, LONG))pDispParams->rgvarg->lVal;
    			LONG lResult = pfn(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[2].lVal);
    
    			if (pVarResult) {
    				pVarResult->vt = VT_I4;
    				pVarResult->lVal = lResult;
    			}
    
    		} else {
    			return DISP_E_MEMBERNOTFOUND;
    		}
    
    		return S_OK;
    	}
    
    };
    
    struct TThreadData {
    	IStream *pStm;
    	IDispatch *pObj;
    	LPVOID pfn;
    	LONG l1;
    	LONG l2;
    };
    
    CCallbackObj m_cObj;
    
    BOOL WINAPI DllMain(HINSTANCE, DWORD,LPVOID){
    	return TRUE;
    }
    
    ULONG WINAPI ThreadProc(TThreadData *pData) {
    	DISPPARAMS tParams;
    	VARIANTARG tVarArgs[3];
    	VARIANT tVarResult = {VT_EMPTY};
    
    	if (pData->pObj) {
    
    		tParams.cArgs = 3;
    		tParams.cNamedArgs = 0;
    		tParams.rgdispidNamedArgs = NULL;
    		tParams.rgvarg = tVarArgs;
    
    		tVarArgs[0].vt = VT_I4; tVarArgs[0].lVal = (LONG)pData->pfn;
    		tVarArgs[1].vt = VT_I4; tVarArgs[1].lVal = pData->l1;
    		tVarArgs[2].vt = VT_I4; tVarArgs[2].lVal = pData->l2;
    
    		pData->pObj->Invoke(1, GUID_NULL, GetUserDefaultLCID(), DISPATCH_METHOD, &tParams, &tVarResult, NULL, NULL);
    		pData->pObj->Release();
    
    	}
    
    	GlobalFree(pData);
    
    	return 0;
    
    }
    
    ULONG WINAPI Unmarshal(TThreadData *pData) {
    
    	HRESULT hr;
    	hr = CoGetInterfaceAndReleaseStream(pData->pStm, IID_IDispatch, (void**)&pData->pObj);
    	pData->pStm = NULL;
    
    	return hr;
    
    }
    
    extern "C" HRESULT WINAPI CreateThreadAndCallback(LPVOID pfn, LONG l1, LONG l2) {
    	HRESULT hr = S_OK;
    	TThreadData *pData = (TThreadData*)GlobalAlloc(GMEM_ZEROINIT, sizeof(TThreadData));
    
    	if (!pData)
    		return E_OUTOFMEMORY;
    
    	pData->pfn = pfn;
    	pData->l1 = l1;
    	pData->l2 = l2;
    
    	if (FAILED(hr = CoMarshalInterThreadInterfaceInStream(IID_IDispatch, &m_cObj, &pData->pStm))) {
    		GlobalFree(pData);
    		return hr;
    	}
        hMod = LoadLibrary ("shlwapi.dll"); 
     if (hMod != NULL) 
     { 
     fSHCreateThread = (BOOL (*)( LPTHREAD_START_ROUTINE , VOID *, DWORD , LPTHREAD_START_ROUTINE)) GetProcAddress (hMod, "SHCreateThread");
    
    	if (!fSHCreateThread((LPTHREAD_START_ROUTINE)ThreadProc, pData, CTF_COINIT_STA, (LPTHREAD_START_ROUTINE)Unmarshal)) {
    		pData->pStm->Release();
    		GlobalFree(pData);
    		hr = E_FAIL;
    	}
    
    	return hr;
     }
    }
    1. When set the build in debug mode and build dll and run vb6 std exe after displaying the correct msgbox and
    when we click on the ok button of this msgbox I get the following error message box and app crashes.

    ---------------------------
    Microsoft Visual C++ Debug Library
    ---------------------------
    Debug Error!

    Program: ...\cb\Project1.exe
    Module:
    File: i386\chkesp.c
    Line: 42

    The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.

    (Press Retry to debug the application)
    ---------------------------
    Abort Retry Ignore
    ---------------------------


    2. When set the build in release mode and build dll and run vb6 std exe the app immediately app crashes.

    Full project attached.

    Thanks
    Attached Files Attached Files

  24. #24

  25. #25

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    The crash got solved using_stdcall .

    The MsgBox is displayed in main STA thread.

    If we use modmultithreading module directly in the vb6 standard exe and call callback of vb6 standard exe from new thread of c/c++ standard dll using initcurrentthreadandcallfunction in callback we are achieving real threading in the sense the MsgBox is displayed in new thread without blocking main STA.

    Now without using modmultithreading module in vb6 standard exe CAN we run MsgBox callback in c/c++ dll new thread without blocking main STA as above using SHCreateThread so that we can use this for any standard vb6 exe callback.

    Thanks

  26. #26

  27. #27

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by The trick View Post
    I've already written it in #18 post.

    It's possible but it requires to change the module to attach to a compiled project. BUT you can't just call already compiled code which doesn't expect threading. The most probably result is the crash.
    Hello Trick,

    Already as you know ,


    From a loaded vb6 activex dll in single threaded mode (using modMultiThreading2.bas) we can create a new thread and execute module function of any vb6 standard exe as follows :



    hModule = GetModuleHandle(0) //get module instance of vb6 standard exe in which the dll is loaded

    ' Get VBHeader as follows

    ' // Get e_lfanew
    GetMem4 ByVal hModule + &H3C, ptr
    ' // Get AddressOfEntryPoint
    GetMem4 ByVal ptr + &H28 + hModule, ptr
    ' // Get VBHeader
    GetMem4 ByVal ptr + hModule + 1, pVBHeader



    'initialize STA and context in threadproc of new thread created using CreateThread API

    CreateIExprSrvObj 0&, 4&, 0&
    CoInitialize ByVal 0&


    pNewHeader=CreateVBHeaderCopy

    If pVBHeader Then
    VBDllGetClassObject hModule , 0& , pNewHeader, aiid, tGuid, 0&
    End If

    Now we can list all modules and select one of the modules say module1 and its functions(callbacks) thru the above pVBHeader(as in CreatePrivateClass),
    and run them using Dispcallfunc in this new thread.(Same method is used for CreateObjectbynameinnewthread)


    Similarly can we load a c/c++ dll instead of vb6 activex dll as above ,create a new thread using CreateThread or SHCreateThread and in the new thread of c/c++ dll call the module callback of any vb6 standard exe using a simple example c/c++ code like standard win32 c/c++ dll code.

    Thanks

  28. #28

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    Any vb6 standard exe works fine in compiled mode when I use vbcallback single threaded activex dll which has multiuse class Class1 as follows:


    Code:
    Class1.cls code of vbcallback activex dll
    Public Function InitCurrentThreadAndCallFunction( _
                    ByVal pfnCallback As Long, _
                    ByVal pParam As Long, _
                    ByRef lReturnValue As Long) As Boolean
      modMultiThreading.InitCurrentThreadAndCallFunction pfnCallback, _
                    pParam, _
                    lReturnValue
                    
    End Function
    
    Private Sub Class_Initialize()
    modMultiThreading.Initialize 0
    End Sub
    
    Private Sub Class_Terminate()
    modMultiThreading.Uninitialize
    End Sub
    Thru references when we add vbcallback.dll to any vb6 standard exe say genericvb6stdproj and call InitCurrentThreadAndCallFunction as follows:


    Code:
    Public Function MyCallBackFunction() As Long 'ByVal myIntArg As Long, ByVal myIntArg2 As Long) As Long
    vbcallbackobj.InitCurrentThreadAndCallFunction AddressOf abc, 0, 0
    End Function
    
    Public Function abc() As Long 
      
        MsgBox "in vbcallback"' is displayed in new thread
        abc = 0
        
    End Function



    in frmMain.frm of generic standard exe we have





    Code:
    Private Declare Sub CallFunctionPointer Lib "cstddll.dll" _
            (ByVal lpfnMyFunc As Long)
    Private Sub Command1_Click()
    CallFunctionPointer AddressOf MyCallBackFunction
    End Sub
    
    Private Sub Command2_Click()
    vbcallbackobj.CreatePrivateObjectByNameInNewThread "Form2"
    
    End Sub
    
    Private Sub Form_Load()
    'modMultiThreading.Initialize
     Set vbcallbackobj = New Class1
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
    'modMultiThreading.Uninitialize
    End Sub

    CallFunctionPointer is exported from our cstddll win32 c/c++ standard dll.It takes module function pointer as parameter and executes it in
    new thread using CreateThread API.



    So our vb6 standard exe interacts with cstddll(win32 c/c++ dll) as well as vbcallback singlethreaded actx dll for calling initcurrentthreadand callfunction
    using Class1 object.


    So our generic vb6 std exe is not depending on modmultithreading2.bas.It uses vbcallback actx dll for achieving this.

    So now if we can move the functionality of vbcallback actx dll into our cstddll c/++ dll we will be having a single c/c++ dll with which we can interact with for achieving real multithreading ie. the MsgBox is displayed in new thread which we can check by moving main form after displaying the MsgBox for which I request your help.

    code attached

    Thanks.
    Attached Files Attached Files

  29. #29

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    Since cstddll is a c/c++ standard dll exporting "CallFunctionPointer",instead of using multiuse class I converted the vbcallback standard actx dll(single threaded) to export

    functions Initialize ,Uninitialize ,InitCurrentThreadAndCallFunction by using DLLMain and in generic vb6 std exe(genericstdvb6exe) imported them as follows:


    Code:
    Public Declare Function Initialize Lib "vbcallback.dll" _
            (hinstance As Long) As Boolean
            
    
    Public Declare Sub Uninitialize Lib "vbcallback.dll" _
            ()
    Public Declare Function InitCurrentThreadAndCallFunction Lib "vbcallback.dll" _
                   (ByVal pfnCallback As Long, _
                   ByVal pParam As Long, _
                    ByRef lReturnValue As Long) As Boolean


    and called them as follows:

    module1.bas code

    Code:
    Public Function MyCallBackFunction() As Long 'ByVal myIntArg As Long, ByVal myIntArg2 As Long) As Long
    
    InitCurrentThreadAndCallFunction AddressOf abc, 0, 0 'calling exported function
    
    End Function
    frmMain.frm code

    Code:
    Private Declare Sub CallFunctionPointer Lib "cstddll.dll" _
            (ByVal lpfnMyFunc As Long)
    
    Private Sub Command1_Click()
    CallFunctionPointer AddressOf MyCallBackFunction
    End Sub
    
    
    
    Private Sub Form_Load()
    Initialize 0 'exported function
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
    Uninitialize 'exported function
    End Sub
    So now myprojupdated folder consists of three projects:

    1.genericstdvb6exe
    2.cstddll c/c++ dll
    3.vbcallback activex dll single threaded now exporting Initialize ,Uninitialize ,InitCurrentThreadAndCallFunction.

    genericstdvb6exe works fine.

    Now you help me how to convert the above Initialize ,Uninitialize ,InitCurrentThreadAndCallFunction vbcallback activex dll exported functions to c/c++ code.

    Thanks

    code attached
    Attached Files Attached Files

  30. #30
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,057

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    [….]
    Last edited by dz32; Mar 4th, 2022 at 07:31 AM.

  31. #31
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    What is this VB6 application that it is worth you spending this much effort on it? You've been spending months on some accessing some functionality, and it isn't working. I'm inclined to say that you're spending too much time on this for it to be justified. Perhaps there is a reason, but it sure doesn't look like it. You really need to step back and think this over before you continue bashing your head against this wall. So, what is the VB6 application? Why can't you replicate the functionality rather than even make use of the VB6 application?
    My usual boring signature: Nothing

  32. #32

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by Shaggy Hiker View Post
    What is this VB6 application that it is worth you spending this much effort on it? You've been spending months on some accessing some functionality, and it isn't working. I'm inclined to say that you're spending too much time on this for it to be justified. Perhaps there is a reason, but it sure doesn't look like it. You really need to step back and think this over before you continue bashing your head against this wall. So, what is the VB6 application? Why can't you replicate the functionality rather than even make use of the VB6 application?
    Dear Shaggy Hiker,

    I was trying to write a c/c++ dll which can call vb6 module callback of vb6 standard exe.I was experiencing a crash for which I took help of Trick with your permission.Trick gave me solution for the same except for private objects of vb6 std exe.But when I explored Tricks multithreading example for standard exe it worked well even for private objects .
    So I tried using a vb6 activex dll single threaded in two ways for the same vb6 std exe as follows:

    1.using multiuse class and its member functions.

    2.using exported functions of vb6 directly using DLLMain initialization.

    Both worked fine.

    Now my last problem was why I am not able to solve the problem in c/c++ dll when I was able to solve it using vb6 actx dll for the same vb6 std exe.

    If I am able to implement the exported function of vb6 vbcallback activex dll in
    c/c++ I can put it directly in my c/++ dll and avoid two dlls.

    This will help me develop applications in c/c++ which require multithreading explicitly as I did using createthread api and using readymade threading applications like using Multimedia timers for which the callbacks are by default multithreaded.

    Dear Trick,

    Having helped me upto this level in vb6,c/c++ and COM I think if you can provide me c/c++ code for InitCurrentThreadAndCallFunction exported function of vbcallback single threaded activex dll
    I will mark this thread as resolved and thank you both Shaggy and Trick for the same.

    Thanks

  33. #33
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by smkperu View Post
    This will help me develop applications in c/c++ which require multithreading explicitly...
    Not sure, how VB6 enters the picture here...

    VB6-threading is COM(STA)-based - whereas in C/C++ you can use "free-threading" directly -
    (without any VB6 interaction, or any help from "ported VB6-codesnippets").

    IMO you're asking your questions in the wrong forum (wasting everyones time).

    Olaf

  34. #34

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Quote Originally Posted by Schmidt View Post
    Not sure, how VB6 enters the picture here...

    VB6-threading is COM(STA)-based - whereas in C/C++ you can use "free-threading" directly -
    (without any VB6 interaction, or any help from "ported VB6-codesnippets").

    IMO you're asking your questions in the wrong forum (wasting everyones time).

    Olaf
    Hello Olaf,

    This will help me develop applications in c/c++ which require multithreading explicitly impiles that using CreateThread in the context of vb6 standard exe thru c/c++ dll which I did in my c/c++ code .

    Thanks.

  35. #35
    Frenzied Member
    Join Date
    Jun 2015
    Posts
    1,057

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    just curious, do you have an actual project that needs this? I can not imagine a scenario where I would ever need this.

    It sounds like the primary use would be a vb6 developer who wants to run his code in multiple threads. Not sure why
    a C++ developer would go through all of this complexity just to reuse some vb6 code.
    Last edited by dz32; Mar 4th, 2022 at 05:03 PM.

  36. #36
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    It sure seems like you are making this vastly harder than it needs to be. C/C++ can do plenty of threading. VB6 can also do threading, though it takes a bit of work to get there. Why would you want to combine the two? If you're working in C/C++, then work in C/C++. You can do everything in there that you can do in VB6, and avoid this whole headache of trying to meld two languages. The great advantage of VB6 is rapid development and a nice form designer. You've kind of thrown out the first of those advantages when you decided to go with C/C++, so that leaves only the nice form designer. Forms and threading don't often play very nicely together, so the form designer alone doesn't seem sufficient reason for you to meld the two languages, which means there just isn't a good reason to do this. You can do multi-threading in VB6, you can do multi-threading in C/C++, you can also create forms in both (though it seems that this is still easier in VB6 for some reason), so what's the point of melding the two when it causes such pain?
    My usual boring signature: Nothing

  37. #37
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Well you could always use C#/VB.Net and get the best of both worlds, easy threading and rapid GUI development.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  38. #38

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello The Trick,

    I called exported InitCurrentThreadAndCallFunction from vbcallback.dll directly from c/c++ dll cbdll using
    SHCreateThread by using the example provided by you in #20 as follows:


    Code:
    // cdll.cpp : Defines the entry point for the DLL application.
    //
    
    #include "stdafx.h"
    
    #include <Windows.h>
    #include <shlwapi.h>
    #include <ole2.h>
    #include <objbase.h>
    //#include <comdef.h>
    #include <atlbase.h>
    
    #include <stdio.h>
    IDispatch *IDisp;
    IUnknown *IUnk;
    
    typedef BOOL (pInitCurrentThreadAndCallFunction)( int, int,int*);
    BOOL (*fInitCurrentThreadAndCallFunction)( int, int,int*);
    //declare a function pointer to call a funciton
    		  // that returns an int 
    		  int ( __stdcall *lpVBFunction ) (int,int);
    		  long glpfnTest;
    #pragma comment (lib, "Shlwapi.lib")
    #pragma comment (linker, "/EXPORT:CreateThreadAndCallback=_CreateThreadAndCallback@12")
    #define CTF_COINIT_STA  0x00000008
    typedef BOOL ( WINAPI tSHCreateThread)( LPTHREAD_START_ROUTINE , VOID *,  DWORD,  LPTHREAD_START_ROUTINE );
    //tSHCreateThread  fSHCreateThread;
    
    BOOL (WINAPI *fSHCreateThread)( LPTHREAD_START_ROUTINE , VOID *,  DWORD,  LPTHREAD_START_ROUTINE );
    HMODULE hMod,hMod1; 
     
     
     
    class CCallbackObj : public IDispatch {
    	
    private:
    
    	ULONG m_uRefCtr;
    
    public:
    
    	HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void  **ppvObject) {
    
    		if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IDispatch)) {
    			*ppvObject = (void*)this;
    		} else {
    			return E_NOINTERFACE;
    		}
    
    		return S_OK;
    
    	}
    
        ULONG STDMETHODCALLTYPE AddRef() {
    		return m_uRefCtr++;
    	}
    
        ULONG STDMETHODCALLTYPE Release() {
    		return m_uRefCtr--;
    	}
    
        HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) {
    		return E_NOTIMPL;
    	}
            
        HRESULT STDMETHODCALLTYPE GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) {
    		HRESULT hr = S_OK;
    
    		for (UINT i = 0; i < cNames; i++) {
    			if (lstrcmpW(rgszNames[i], L"CallFunc") == 0) {
    				rgDispId[i] = 1;
    			} else {
    				rgDispId[i] = DISPID_UNKNOWN;
    				hr = DISPID_UNKNOWN;
    			}
    		}
    
    		return hr;
    	}
            
        HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags,
                 						 DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) {
    
    		if (!IsEqualGUID(GUID_NULL, riid))
    			return DISP_E_UNKNOWNNAME;
    
    		if (wFlags & DISPATCH_METHOD) {
    
    			if (!pDispParams)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (!pDispParams->rgvarg)
    				return DISP_E_PARAMNOTOPTIONAL;
    
    			if (pDispParams->cArgs != 3)
    				return DISP_E_BADPARAMCOUNT;
    
    			if (dispIdMember != 1)
    				return DISP_E_MEMBERNOTFOUND;
    
    			if (pDispParams->rgvarg->vt != VT_I4)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[1].vt != VT_I4 && pDispParams->rgvarg[1].vt != VT_I2 && pDispParams->rgvarg[1].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			if (pDispParams->rgvarg[2].vt != VT_I4 && pDispParams->rgvarg[2].vt != VT_I2 && pDispParams->rgvarg[2].vt != VT_UI1)
    				return DISP_E_TYPEMISMATCH;
    
    			LONG (__stdcall *pfn)(LONG, LONG) = (LONG(__stdcall *)(LONG, LONG))pDispParams->rgvarg->lVal;
    			LONG lResult = pfn(pDispParams->rgvarg[1].lVal, pDispParams->rgvarg[2].lVal);
    
    			if (pVarResult) {
    				pVarResult->vt = VT_I4;
    				pVarResult->lVal = lResult;
    			}
    
    		} else {
    			return DISP_E_MEMBERNOTFOUND;
    		}
    
    		return S_OK;
    	}
    
    };
    
    struct TThreadData {
    	IStream *pStm;
    	IDispatch *pObj;
    	LPVOID pfn;
    	LONG l1;
    	LONG l2;
    };
    
    CCallbackObj m_cObj;
    
    BOOL WINAPI DllMain(HINSTANCE, DWORD,LPVOID){
    	return TRUE;
    }
    
    ULONG WINAPI ThreadProc(TThreadData *pData) {
    	DISPPARAMS tParams;
    	VARIANTARG tVarArgs[3];
    	VARIANT tVarResult = {VT_EMPTY};
    
    	int retVal;
    
    	if (pData->pObj) {
    
    		tParams.cArgs = 3;
    		tParams.cNamedArgs = 0;
    		tParams.rgdispidNamedArgs = NULL;
    		tParams.rgvarg = tVarArgs;
    
    		tVarArgs[0].vt = VT_I4; tVarArgs[0].lVal = (LONG)pData->pfn;
    		tVarArgs[1].vt = VT_I4; tVarArgs[1].lVal = pData->l1;
    		tVarArgs[2].vt = VT_I4; tVarArgs[2].lVal = pData->l2;
    
    	    fInitCurrentThreadAndCallFunction((long)pData->pfn,0,&retVal);//crashes at this line
    		pData->pObj->Release();
    
    	}
    
    	GlobalFree(pData);
    
    	return 0;
    
    }
    
    ULONG WINAPI Unmarshal(TThreadData *pData) {
    
    	HRESULT hr;
    	hr = CoGetInterfaceAndReleaseStream(pData->pStm, IID_IDispatch, (void**)&pData->pObj);
    	pData->pStm = NULL;
    
    	return hr;
    
    }
    
    extern "C" HRESULT WINAPI CreateThreadAndCallback(LPVOID pfn, LONG l1, LONG l2) {
    	HRESULT hr = S_OK;
    	TThreadData *pData = (TThreadData*)GlobalAlloc(GMEM_ZEROINIT, sizeof(TThreadData));
    
    	if (!pData)
    		return E_OUTOFMEMORY;
    
    	pData->pfn = pfn;
    	pData->l1 = l1;
    	pData->l2 = l2;
    
    
          pData->pObj=IDisp;
    
    
    	if (FAILED(hr = CoMarshalInterThreadInterfaceInStream(IID_IDispatch,&m_cObj, &pData->pStm))) {
    		GlobalFree(pData);
    		return hr;
    	}
    	 hMod1 = LoadLibrary ("vbcallback.dll"); 
     if (hMod1 != NULL) 
      
     fInitCurrentThreadAndCallFunction = (BOOL (*)( int, int,int*)) GetProcAddress (hMod1, "InitCurrentThreadAndCallFunction");
    
    
        hMod = LoadLibrary ("shlwapi.dll"); 
     if (hMod != NULL) 
     { 
     fSHCreateThread = (BOOL (WINAPI *)( LPTHREAD_START_ROUTINE , VOID *, DWORD , LPTHREAD_START_ROUTINE)) GetProcAddress (hMod, "SHCreateThread");
    
    	if (!fSHCreateThread((LPTHREAD_START_ROUTINE)ThreadProc, pData, CTF_COINIT_STA, (LPTHREAD_START_ROUTINE)Unmarshal)) {
    		pData->pStm->Release();
    		GlobalFree(pData);
    		hr = E_FAIL;
    	}
    
    	return hr;
     }
    }
    I am passing the pData->pfn(vb6 std exe callback) to exported InitCurrentThreadAndCallFunction (in vbcallback actx singlr threaded dll) as parameter instead of calling it directly (using IDispatch invoke()).

    But still it crashes at the follwing line in the above code.
    fInitCurrentThreadAndCallFunction((long)pData->pfn,0,&retVal);//crashes at this line

    How to avoid the crash and make the MsgBox display the addition result properly using this InitCurrentThreadAndCallFunction call .


    Project attached.

    cbproj folder contains

    1.cb (vb std exe project)

    2.In cb folder we have cbdll folder which is c/c++ dll project

    3.vbcallback (vb activex single threaded dll exporting InitCurrentThreadAndCallFunction function)


    Thanks
    Last edited by Shaggy Hiker; Mar 10th, 2022 at 09:35 AM. Reason: Removed attachment that contained compiled code.

  39. #39

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,

    The problem got solved when I called exported Initialize() function of vbcallback1 single threaded actx dll before calling SHCreateThread() in CreateThreadAndCallback().


    Code:
    extern "C" HRESULT WINAPI CreateThreadAndCallback(LPVOID pfn, LONG l1, LONG l2) {
    	HRESULT hr = S_OK;
    	TThreadData *pData = (TThreadData*)GlobalAlloc(GMEM_ZEROINIT, sizeof(TThreadData));
    
    	if (!pData)
    		return E_OUTOFMEMORY;
    
    	pData->pfn = pfn;
    	pData->l1 = l1;
    	pData->l2 = l2;
    
    
          pData->pObj=IDisp;
    
    
    	if (FAILED(hr = CoMarshalInterThreadInterfaceInStream(IID_IDispatch,&m_cObj, &pData->pStm))) {
    		GlobalFree(pData);
    		return hr;
    	}
    	 hMod1 = LoadLibrary ("vbcallback.dll"); 
     if (hMod1 != NULL) 
      
     fInititialize = (BOOL (WINAPI  *)( )) GetProcAddress (hMod1, "Inititialize");
     fInitCurrentThreadAndCallFunction = (BOOL (WINAPI  *)( int, int,int*)) GetProcAddress (hMod1, "InitCurrentThreadAndCallFunction");
    
    
        hMod = LoadLibrary ("shlwapi.dll"); 
     if (hMod != NULL) 
     { 
     fSHCreateThread = (BOOL (WINAPI *)( LPTHREAD_START_ROUTINE , VOID *, DWORD , LPTHREAD_START_ROUTINE)) GetProcAddress (hMod, "SHCreateThread");
    fInititialize(0);
    	if (!fSHCreateThread((LPTHREAD_START_ROUTINE)ThreadProc, pData, CTF_COINIT_STA, (LPTHREAD_START_ROUTINE)Unmarshal)) {
    		pData->pStm->Release();
    		GlobalFree(pData);
    		hr = E_FAIL;
    	}
    
    	return hr;
     }
    }
    Only final task left is vbcallback1 vb6 actx single threaded dll which is exporting Initialize ,Unitialize and InitCurrentThreadandCallFunction has to be implemented in c/c++.

    Thanks

  40. #40

    Thread Starter
    Addicted Member
    Join Date
    Dec 2021
    Posts
    144

    Re: how to call a vb6 standalone exe module function from c/c++ new thread-reg

    Hello Trick,


    The vbcallback1.dll which is exporting Initialize,Unitialize and InitCurrentThreadandCallFunction functions

    executes the following code (ModDllMain.bas) when it is loaded into our cbdll c/c++ dll using LoadLibrary("vbcallback1.dll") thru DLL_PROCESS_ATTACH:


    Code:
     
            CreateIExprSrvObj 0, 4, 0
            CoInitialize 0
            lpDllGetObj = GetProcAddress(hinstDLL, "DllGetClassObject")
            CopyMemory gvbtab, ByVal lpDllGetObj + 2, 4
            CopyMemory gvb, ByVal lpDllGetObj + 7, 4
            CopyMemory gloaders, ByVal lpDllGetObj + 12, 4
            With riid
                .Data1 = 1
                .Data4(0) = &HC0
                .Data4(7) = &H46
            End With
            DllMain = UserDllMain(gloaders, gvb, hinstDLL, fdwReason, lpvReserved)
            VBDllGetClassObject gloaders, gvb, gvbtab, aiid, riid, ofac
            CopyMemory gvbtab, ByVal lpDllGetObj + 2, 4
            CopyMemory gvb, ByVal lpDllGetObj + 7, 4
            CopyMemory gloaders, ByVal lpDllGetObj + 12, 4
    Once the above code executes we can use the exported functions using GetProcAddress which I did in cbdll.

    Now can suggest me a way to convert the same vbcallback1 dll to c/c++ dll with same functionality.

    vbcallback1 vb6 actx single threaded dll code attached

    Thanks
    Attached Files Attached Files
    Last edited by smkperu; Mar 10th, 2022 at 05:55 AM.

Page 1 of 2 12 LastLast

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