Announcement

Collapse
No announcement yet.

Process in background (called from VB)

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Mike Doty
    replied
    CallBack to Visual Basic and SendMessage to Visual Basic

    This shows how to directly SendMessage to a VB control.
    It also shows how to use a callback.
    All the code is in a PowerBASIC DLL.

    VB code is in the second code window.

    If no errors reported will post in source code forum.


    Code:
    #DIM ALL
    #COMPILE DLL 'subDLL.bas
    #INCLUDE "win32api.inc"
    %CallBack = &H300
    GLOBAL gControl    AS LONG
    GLOBAL gPrevWndProc AS LONG
    SUB TestCallBack EXPORT
      IF gControl = 0 THEN ? "Subclass is not on":EXIT SUB
      STATIC Counter AS LONG
      LOCAL s       AS STRING
      LOCAL wParam  AS DWORD
      LOCAL lParam  AS LONG
      INCR Counter
      s = "Callback string" + STR$(Counter)
      wParam = LEN(s)
      lParam = STRPTR(s)
      SendMessage gControl,%Callback,wParam,lParam 'let callback handle it
    END SUB
    SUB TestSendMessage(hControl AS LONG) EXPORT
      STATIC Counter AS LONG
      LOCAL s AS STRING
      INCR Counter
      s = "SendMessage direct to control" + STR$(counter)
      SendMessage(hControl,%LB_ADDSTRING,%NULL,STRPTR(s))
    END SUB
    FUNCTION Peeker(Address AS DWORD, Characters AS LONG) EXPORT AS STRING
      'Could be used by VB to read string in memory
      FUNCTION  = PEEK$(address???, characters)
    END FUNCTION
    SUB EndSubClass() EXPORT
      CALL SetWindowLong(gControl, %GWL_WNDPROC, gPrevWndProc)  'restore old address
    END SUB
    FUNCTION WindowProc(BYVAL hControl AS LONG, BYVAL uMsg AS LONG, _
        BYVAL wParam AS LONG, BYVAL lParam AS LONG) AS LONG
     
      DIM sPeekString AS STRING  'string already in memory
      IF uMsg = %CALLBACK THEN  'triggered callback
        sPeekString = PEEKER(lParam, wParam)
        SendMessage gControl,%LB_ADDSTRING,%NULL,STRPTR(sPeekString)
        EXIT FUNCTION
      END IF
      FUNCTION = CallWindowProc(gPrevWndProc, hControl, uMsg, wParam, lParam)
    END FUNCTION
    SUB StartSubClass(hControl AS LONG) EXPORT
      gControl = hControl
      gPrevWndProc = SetWindowLong(gControl, %GWL_WNDPROC, CODEPTR (WindowProc))
    END SUB
    Code:
    #IF 0
     REM VB6
     REM  1. Add a Command button
     REM  2. Add a listbox
    PRIVATE DECLARE SUB TESTSENDMESSAGE LIB "subdll" (hControl AS LONG)
    PRIVATE DECLARE SUB TESTCALLBACK LIB "subdll" ()
    PRIVATE DECLARE SUB STARTSUBCLASS LIB "subdll" (hControl AS LONG)
    PRIVATE DECLARE FUNCTION ENDSUBCLASS LIB "subdll" () AS LONG
    OPTION EXPLICIT
    PRIVATE SUB Command1_Click()
      TESTSENDMESSAGE List1.hWnd
      TESTCALLBACK
    END SUB
    PRIVATE SUB Form_Load()
      CHDIR App.Path
      Command1.Caption = "Test"
      STARTSUBCLASS List1.hWnd
    END SUB
    PRIVATE SUB Form_QueryUnload(CANCEL AS INTEGER, UnloadMode AS INTEGER)
      ENDSUBCLASS 'required if subclassing
    END SUB
    #ENDIF
    Can now work in the VB6 environment with threads, callbacks and sendmessage.
    Last edited by Mike Doty; 7 Jun 2009, 08:41 PM.

    Leave a comment:


  • Mike Doty
    replied
    Read back strings from PB to VB using SendMesssage
    Added Peeker function to DLL below.
    s = PEEKER(lParam, wParam) 'in VB6 callback

    Code:
     
     
    GLOBAL gForm1 AS LONG
     
    SUB Init(Form1 AS DWORD) EXPORT
      gForm1 =Form1
    END SUB
    
    SUB CallVb EXPORT
      LOCAL s AS STRING        'use STRPTR with dynamic strings
      LOCAL lResult   AS LONG
      LOCAL dwHandle  AS DWORD
      LOCAL dwMsg     AS DWORD
      LOCAL dwParam   AS DWORD
      LOCAL lParam    AS LONG
      dwHandle = gForm1
      dwMsg    = &H300
      s = "TEST COMPLETE"
      dwParam = LEN(s)      'length
      lParam = STRPTR(s)    'address
      lResult = SendMessage(dwHandle,dwMsg,dwParam,lParam)
    END SUB
    
    FUNCTION Peeker(Address AS DWORD, Characters AS LONG) EXPORT AS STRING
      FUNCTION  = PEEK$(address???, characters)
    END FUNCTION
    Last edited by Mike Doty; 7 Jun 2009, 04:19 PM. Reason: Changed from ASCIIZ to Dynamic strings

    Leave a comment:


  • Mike Doty
    replied
    Threads are working fine with VB6.

    Now about callbacks.

    http://www.thescarms.com/vbasic/subclassform.aspx 'subclassing code for VB6
    http://www.thevbzone.com/secrets.htm 'pointers with VB6
    Last edited by Mike Doty; 7 Jun 2009, 04:08 PM. Reason: Got chopped off

    Leave a comment:


  • Michael Mattias
    replied
    >The cb may not be invoked from inside the pb thread, that's all.

    ??

    Why not, if that call will not do anything which might itself cause an re-entrant call? This is really the crux of my question about how VB works.

    Given:
    Code:
    ' MY_VB_PROGRAM.BAS 
    FUNCTION MainWindowProcedure (hWnd, uMSG, wParam, lparam) 
    ......
         hThread = CreateThread (params, calling MY_VB_FUNCTION ) 
         ' returns immediately with a thread handle 
    
    ....
    
    FUNCTION My_VB_FUNCTION (params) 
       CALL MyFunctionInmyPBDLL (params) 
       PostMessage  primaryHWnd, private_message_no, _
          params  with results of MyFunctionInMyPbDLL
    END FUNCTION
    You don't even need a callback here. Since CreateThread returns essentially immediately, the primary GUI thread is not blocked and should run normally, shouldn't it?

    ???

    It's really not a whole lot different from the "ALL PB" demo at GUI + Worker Thread + Abort Demo 11-24-07, at least as far as I can tell.

    MCM

    Leave a comment:


  • Mike Doty
    replied
    John,
    Having no problems in that area.
    Actually, everything seems to be working after using
    DIALOG DO EVENTS and not ending the function that created the thread.

    I guess this forum thread could be closed, but the different ways to
    return data from the DLL to VB are of the most interest now.
    I will be using the code in post #10.

    I'm trying to write all code in PB with a VB front end for COM.

    Note: Just noted a potential problem, not shown.
    I set a global gBusy if something is processing a request.
    This will be modified because it is possible that another function
    could reset the flag. The gBusy flag will become a counter that
    is increased and decreased to indicate something is in progress.
    Instead of setting gBusy = 1, gBusy = gBusy + 1
    and gBusy = gBusy - 1 instead of gBusy = 0.



    Code:
    'PB
    FUNCTION GetThreadCount EXPORT AS LONG 'VB uses to not end until threadcount = 1
      FUNCTION = THREADCOUNT
    END FUNCTION 
    'VB
    Private Sub Form_QueryUnload(Cancel As Integer, UnloadMode As Integer)
      If GETTHREADCOUNT > 1 Then
        Splash "Background function is processing", 1000
        Cancel = True
      ElseIf gBusy Then
        Splash "Processing a request", 1000
        Cancel = True
      End If
     
    End Sub
    Last edited by Mike Doty; 30 May 2009, 03:56 PM.

    Leave a comment:


  • John Petty
    replied
    Mike
    I am probably out of my depth here and I am not sure I understand your problem but here goes. VB6 has no understanding of threads and cannot be used to control them. When you end a VB program by most means you can respond to a (from memory) Form Unload Event of the main form. In that event you should be quering the DLL as to whether all threads it has created have actually ended or Windows is going to do a dummy spit.
    John

    Leave a comment:


  • John Petty
    replied
    OK we agree, thank you for helping Mike understand my posts which might have caused a problem due to lack of clarity

    Leave a comment:


  • Edwin Knoppert
    replied
    I know that.
    The cb may not be invoked from inside the pb thread, that's all.

    Leave a comment:


  • John Petty
    replied
    Originally posted by Edwin Knoppert View Post
    VB6 does not like the re-entry of threads, the callback part will fail.
    A message is the most ideal imo.

    You could also do something about the non-responsive gui but that subject.. no one takes my hint on that..
    Tried a few times..
    Not really Edwin. Yes VB6 is a single threaded apartment model (or described something like that) so it is very difficult but not impossible to create threads in VB6, however you can create threads in the PB DLL, I assume I didn't describe the situation correctly.
    VB calls a PB DLL with data, The PB DLL spawns threads to process that data. If a thread completes its work it reports that data back to the main DLL thread which passes the results to the VB program via the callback. As I said previously the callback function must return to the DLL before it can do any more work (should I have said before it calls the callback again), thus all VB remains in the single thread it knows exists and is totally unaware that other processing might have taken place in multiple threads or even on other computers.

    Leave a comment:


  • Michael Mattias
    replied
    I'[m] curious if EnterCriticalSection should also be callled before creating the thread?
    You don't even need a critical section here beccause it is not possible for you to get to the statement where you assign sAnswer = g_answer until the thread function has completed, meaning the value cannot possibly change.

    Which brings us to how you know the thread function has completed....
    Code:
    DO
        DIALOG DOEVENTS 0                'allow others time
        THREAD STATUS hThread TO Result  'get thread status
    LOOP WHILE Result = &H103          'loop while running
    I don't know what DIALOG DOEVENTS does (and unless I am mistaken its implementation has changed several times over the past couple of releases of PB/WIN), but this is assuming whatever it does is sufficient for and consistent with whatever a VB program uses to retrieve and dispatch messages from its message queue.

    If it works, fine, but you need to be aware you are operating in uncharted waters here.

    Can anyone give me a straight answer about creating additional TOEs using the CreateThread() API in VB? I can't see how that could screw up the VB program as long as the thread function does not try doing anything with any of the objects managed by the primary TOE of the VB program.

    That is, if the thread function does not even think about doing anything to cause a re-entrancy, should it not work just fine?




    MCM

    Leave a comment:


  • Edwin Knoppert
    replied
    Hint.. oh a while ago, threads have been discussed recently.

    Leave a comment:


  • Mike Doty
    replied
    Edwin, what hint?
    Message is most ideal, I'll work on that.
    The code I posted #10 enables a VB gui that is responsive and has critical sections in the DLL.
    I'm trying to do all threading in PB to eliminate re-entry issues, etc.

    This appears to work. Critical sections are hopefully all in the right place.
    DIALOG DOEVENTS 0 seemed to make VB gui responsiive.


    I've curious if EnterCriticalSection should also be callled before creating the thread?


    Code:
    SUB Background(sQuestion AS STRING, sAnswer AS STRING) EXPORT
     
      LOCAL hThread, Result AS DWORD
      EnterCritical
      g_Question = sQuestion
      LeaveCritical
      THREAD CREATE BackGroundThread(hThread) TO hThread
      IF hThread = 0 THEN ? "Unable to create thread":EXIT SUB
      DO
        DIALOG DOEVENTS 0                'allow others time
        THREAD STATUS hThread TO Result  'get thread status
      LOOP WHILE Result = &H103          'loop while running
      THREAD CLOSE hThread TO Result     'close thread handle
      IF Result = 0 THEN ? "Error closing thread"
      EnterCritical
      sAnswer = g_Answer                 'return answer from background thread
      LeaveCritical
    END SUB
    Last edited by Mike Doty; 30 May 2009, 11:53 AM.

    Leave a comment:


  • Edwin Knoppert
    replied
    VB6 does not like the re-entry of threads, the callback part will fail.
    A message is the most ideal imo.

    You could also do something about the non-responsive gui but that subject.. no one takes my hint on that..
    Tried a few times..

    Leave a comment:


  • Michael Mattias
    replied
    I was thinking you would want to quickly allow the thread function to terminate, so in your callback you would do something that did not take very long... like posting a message.

    Besides, you have to remember the callback procedure is executing in the context of the additional thread of execution, and depending on your code you may have data integrity issues with which you must be concerned.

    By using the callback function ONLY to post a message, you A) exit quicky, B) ensure you don't have any reentrancy issues and C) ensure that all the "thread function has completed" activities are performed only when any concurrent GUI activities (e.g, the notification message currently being processed in the primary TOE) have been completely processed.

    MCM

    Leave a comment:


  • John Petty
    replied
    Michael
    You have been using SDK too long , no need for messages, VB will let you update values, controls and call form event procedures directly from the callback function (so long as you remember that at some time you must return to the DLL for it to continue its work).
    John

    Leave a comment:


  • Michael Mattias
    replied
    Oooh, oooh, a callback. Why didn't I think of that? That's a good idea. I like it.

    From callback procedure, post a private message to your primary GUI window and there you go. (I assume that's possible; I have not touched VB since VB3. BTW, I do have a VB3 Pro in the box with license card and all the manuals on real paper! Maybe it's fantastically valuable by now.)

    Leave a comment:


  • Mike Doty
    replied
    I'll give that a shot!
    Need to get some sleep. Worked on this all night.
    By the way, the code I posted always works compiled and has never crashed the environment
    No threadcount problem.

    Leave a comment:


  • John Petty
    replied
    Mike
    Fairly simple, only two special requirements, the sub or function must be in a module not a form and must be public. No special limits on the number or type or number of fields passed back other than the usual ones of passing data to and from VB and a DLL. a simple outine as follows
    Public Function MyCallback(variable list as usual) As Long
    pass address to a DLL exported function or sub, say CAddress(Addr as DWORD) as LONG as follows in VB
    Return& = Caddress(AddressOf MyCallback)
    Note AddressOf is key word similar to CODEPTR

    Leave a comment:


  • Mike Doty
    replied
    CALLBACK in VB. I'll have to study to do that. Thank you!

    Code:
    #COMPILE DLL "background.dll"
    #INCLUDE "win32api.inc"
    DECLARE SUB BACKGROUND LIB "background.dll"(sQuestion AS STRING, sAnswer AS STRING)
    GLOBAL gCS AS CRITICAL_SECTION
    GLOBAL g_Question$, g_Answer$
    '--------------------------------------------------------------
    SUB Background(sQuestion AS STRING, sAnswer AS STRING) EXPORT
      'NO SLEEP or WAIT in this function (this in first thrread)
      LOCAL hThread, Result AS DWORD
      EnterCritical
      g_Question = sQuestion
      LeaveCritical
      THREAD CREATE BackGroundThread(hThread) TO hThread
      IF hThread = 0 THEN ? "Unable to create thread":EXIT SUB
      DO
        DIALOG DOEVENTS 0                'allow others time
        THREAD STATUS hThread TO Result  'get thread status
      LOOP WHILE Result = &H103          'loop while running
      THREAD CLOSE hThread TO Result     'close thread handle
      IF Result = 0 THEN ? "Error closing thread"
      EnterCritical
      sAnswer = g_Answer                 'return answer from background thread
      LeaveCritical
    END SUB
    '--------------------------------------------------------------
    THREAD FUNCTION BackGroundThread(BYVAL dummy AS DWORD) AS DWORD
      LOCAL sQuestion AS STRING
      LOCAL sAnswer   AS STRING
      EnterCritical
      sQuestion = g_Question
      LeaveCritical
      'dim simulate as long:for simulate = 1 to 5:SLEEP 1000:BEEP:NEXT 'simulate working
    
      SELECT CASE sQuestion
        CASE ""
          sAnswer = "No question"
        CASE ELSE
          sAnswer = TIME$ + " some answer"
       END SELECT
      EnterCritical
      g_Answer = sAnswer
      LeaveCritical
    END FUNCTION
    '--------------------------------------------------------------
    FUNCTION GetThreadCount EXPORT AS LONG 'VB uses to not end until threadcount = 1
      FUNCTION = THREADCOUNT
    END FUNCTION
    '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    SUB EnterCritical
       EnterCriticalSection gCS
    END SUB
    '--------------------------------------------------------------
    SUB LeaveCritical
       LeaveCriticalSection gCS
    END SUB
    
    FUNCTION LIBMAIN(BYVAL hInstance AS DWORD, _
                     BYVAL lReason AS LONG,    _
                     BYVAL lReserved AS LONG) AS LONG
      SELECT CASE AS LONG lReason
        CASE %DLL_PROCESS_ATTACH
          REM ? "DLL attached"
          InitializeCriticalSection gCS
          LIBMAIN = 1
          EXIT FUNCTION
        CASE %DLL_PROCESS_DETACH
          REM ? "This DLL is about to be unloaded
           DeleteCriticalSection gCS
              EXIT FUNCTION
        CASE %DLL_THREAD_ATTACH
          ' A [New] thread is starting (see THREADID)
          EXIT FUNCTION
        CASE %DLL_THREAD_DETACH
          ' This thread is closing (see THREADID)
          EXIT FUNCTION
      END SELECT
      LIBMAIN = 0 ' Indicate failure to initialize the DLL!
    END FUNCTION
    Last edited by Mike Doty; 30 May 2009, 10:58 AM. Reason: Posting truncated and reduced dialog doevents 0

    Leave a comment:


  • John Petty
    replied
    Mike
    Why not just put a callback function in the VB code.
    John

    Leave a comment:

Working...
X