- Код: Выделить всё
THREAD CREATE ProcName (param) [SUSPEND] TO hThread
THREAD CREATE ProcName (param) [SUSPEND] TO hThread
jangle писал(а):А если использовать API функцию CreateThread?
BION писал(а):jangle писал(а):А если использовать API функцию CreateThread?
Ха, чёт я сразу не сообразил... Тоесть вызвать в PB dll'е, надо поробывать.
#COMPILE DLL "PBTH.dll"
#Dim All
#Include "win32api.inc"
FUNCTION CreateTh (BYVAL proc AS LONG) EXPORT AS LONG
Local hT, lpThreadId As Dword
hT = CreateThread _ ' Create thread from sub
( _
ByVal 0, _ /* Pointer To Thread security attributes*/
ByVal 0, _ // initial Thread stack Size, In bytes
proc, _ // Pointer To Thread Function
ByVal 0, _ // argument For New Thread
ByVal 0, _ // creation flags
ByVal VarPtr(lpThreadId) _ // Pointer To returned Thread identifier
)
' Если убрать две следующие строки,
' функция до падения приложения успешно вернёт lpThreadId
WaitForSingleObject(hT,%INFINITE)
CloseHandle(hT)
FUNCTION = lpThreadId
END FUNCTION
Declare Function CreateTh& Lib "PBTH.DLL" Alias "CREATETH" (proc&)
BION писал(а):BION писал(а):jangle писал(а):А если использовать API функцию CreateThread?
Ха, чёт я сразу не сообразил... Тоесть вызвать в PB dll'е, надо поробывать.
Вобщем, падает
Хз, мож чего неправильно делаю
pbdll:
- Код: Выделить всё
#COMPILE DLL "PBTH.dll"
#Dim All
#Include "win32api.inc"
FUNCTION CreateTh (BYVAL proc AS LONG) EXPORT AS LONG
Local hT, lpThreadId As Dword
hT = CreateThread _ ' Create thread from sub
( _
ByVal 0, _ /* Pointer To Thread security attributes*/
ByVal 0, _ // initial Thread stack Size, In bytes
proc, _ // Pointer To Thread Function
ByVal 0, _ // argument For New Thread
ByVal 0, _ // creation flags
ByVal VarPtr(lpThreadId) _ // Pointer To returned Thread identifier
)
' Если убрать две следующие строки,
' функция до падения приложения успешно вернёт lpThreadId
WaitForSingleObject(hT,%INFINITE)
CloseHandle(hT)
FUNCTION = lpThreadId
END FUNCTION
VB module
- Код: Выделить всё
Declare Function CreateTh& Lib "PBTH.DLL" Alias "CREATETH" (proc&)
hbmp = CreateThread(BYVAL %NULL,32768,CODEPTR(power_knob),0,0,hbmp)
CLoseHandle hbmp
volo писал(а):Попробуй добавить параметр подобно как:
- Код: Выделить всё
hbmp = CreateThread(BYVAL %NULL,32768,CODEPTR(power_knob),0,0,hbmp)
CLoseHandle hbmp
я имею ввиду initial Thread stack Size, In bytes - сделай 32768 байт размер
BION писал(а):volo писал(а):Попробуй добавить параметр подобно как:
- Код: Выделить всё
hbmp = CreateThread(BYVAL %NULL,32768,CODEPTR(power_knob),0,0,hbmp)
CLoseHandle hbmp
я имею ввиду initial Thread stack Size, In bytes - сделай 32768 байт размер
Нет, та же история...
Говорила мама - пиши на Си...
#COMPILE EXE
#DEBUG ERROR ON
#REGISTER NONE
#DIM ALL
#TOOLS OFF
' %NOGDI = 1 ' no GDI (Graphics Device Interface) functions
' %NOMMIDS = 1 ' no Multimedia ID definitions
%NOMMIDS = 1
#INCLUDE "WIN32API.INC"
#INCLUDE "COMDLG32.INC" ' for OpenFileDialog
' Get message from GetLastError as string
FUNCTION SystemErrorMessageText (BYVAL ECode AS LONG) AS STRING
LOCAL Buffer AS ASCIIZ * 255
FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, ECode, %NULL, buffer, SIZEOF(buffer), BYVAL %NULL
FUNCTION = FORMAT$(ECode, "##### ") & Buffer
END FUNCTION
'==[End Windows API Header Files]============================
' --------------------------
' CONTROLS ON MAIN SCREEN
' --------------------------
%ID_TIMER = 101 ' will be continuously updated whilst running
%ID_DATETIME = 102
%ID_FILENAME = 105 ' pick the file to be 'processed'
%ID_FILENAME_BROWSE = 106
%ID_START = 111
%ID_ABORT = 112
%ID_Progress = 115 ' text control telling us what worker thread is doing.
%DLG_STYLE = %WS_VISIBLE OR %WS_SYSMENU OR %WS_MINIMIZEBOX OR %WS_MAXIMIZEBOX
' DIALOG SET USER slots
%DSU_HWND = 1 ' handle to main dialog
%DSU_HTHREAD = 2 ' if true, it's an open handle to an executing worker thread
%DSU_HEVENTREADY = 3 '
%DSU_FILENAME_PTR = 4
$EVENT_ABORT = "Abort_Worker_Thread_Event"
%NUMBER_CYCLES = 10 ' number of times the target file will be 'processed' before thread
' function ends if worker thread not aborted.
%PROGRESS_MODULO = 50 ' update screen every this many records
MACRO m_CreateThreadReadyEvent = CreateEvent (BYVAL %NULL, BYVAL %TRUE, BYVAL %FALSE, BYVAL %NULL)
' private window message:
%PWM_THREAD_FUNCTION_COMPLETED = %WM_USER + 1 ' posted to main window on completion. lPAram= Aborted (True/False)
' -----------------------------------------------------
' PROGRAM ENTRY POINT
' -----------------------------------------------------
FUNCTION WINMAIN (BYVAL hInstance AS LONG, _
BYVAL hPrevInstance AS LONG, _
BYVAL lpCmdLine AS ASCIIZ PTR, _
BYVAL iCmdShow AS LONG) AS LONG
LOCAL hDlg AS LONG
DIALOG NEW %NULL, "Worker Thread With Abort Demo", 10,10, 300,200, %DLG_STYLE TO hDlg
CONTROL ADD LABEL, hDlg, %ID_DATETIME, "", 200, 10, 90, 12
CONTROL ADD TEXTBOX, hDlg, %ID_FILENAME, "", 10, 24, 220,12
CONTROL ADD BUTTON, hDlg, %ID_FILENAME_BROWSE, "&Browse", 241, 24, 40, 14
CONTROL ADD BUTTON, hDLg, %ID_START, "&Start", 10, 40, 40, 14
CONTROL ADD BUTTON, hDlg, %ID_ABORT, "&Abort", 10, 56, 40, 14
CONTROL ADD LABEL, hDlg, %ID_PROGRESS, "Nothing Happening", 10, 80, 280, 48
DIALOG SHOW MODAL hDlg, CALL DlgProc
END FUNCTION ' WinMain
' ----------------------------------------------------------------
' Helper Function to abort the designated worker Thread function.
' Should never be called unless a worker thread is running
' ----------------------------------------------------------------
FUNCTION AbortWorkerThread (BYVAL hWnd AS LONG, BYVAL hThread AS LONG ) AS LONG
LOCAL hEvent AS LONG, szEvent AS ASCIIZ * 128
LOCAL bInherit AS LONG
LOCAL iWait AS LONG
' get handle to abort event so we can signal it
szEvent = $EVENT_ABORT
bInherit = %FALSE ' we will not be passing this handle to any other process
hEvent = OpenEVent (%EVENT_ALL_ACCESS, bINherit, szEvent)
' OpenEvent will fail if the event does not exist; but that means the worker thread is not running;
' meaning either this function was called when it shouldn't have been; OR.... the thread function
' ended 'normally' between the time the abort command is issued and "now."
' However, waiting on the thread handle is safe, since that handle cannot be closed yet.
IF ISTRUE hEvent THEN
SetEvent hEvent
END IF
' wait for the thread function to end (which it may have even before we got here):
iWait = WaitForSingleObject (hThread, %INFINITE)
' close our handle to abort event (clean up)
' The thread handle is closed in DlgProc when this function returns
CloseHandle hEvent
END FUNCTION
'----------------------------------
' MAIN WINDOW PROCESSOR
'----------------------------------
CALLBACK FUNCTION DlgPRoc () AS LONG
LOCAL sText AS STRING
LOCAL hThread AS LONG, hEventReady AS LONG
LOCAL iRet AS LONG
SELECT CASE AS LONG CBMSG
CASE %WM_INITDIALOG
' disable Abort button, Start button waiting for file to be selected
CONTROL DISABLE CBHNDL, %ID_START
CONTROL DISABLE CBHNDL, %ID_ABORT
' set up a timer so we can see that the screen is always updating
' one second (1000 millisecond) update period. We don't need the return value
SetTimer CBHNDL, %ID_TIMER, 1& * 1000&, BYVAL %NULL
CASE %WM_TIMER
' update the timer label with the current time
sText = DATE$ & " " & TIME$
CONTROL SET TEXT CBHNDL, %ID_DATETIME, sText
CASE %WM_SYSCOMMAND
IF (CBWPARAM AND &hFFF0) = %SC_CLOSE THEN
DIALOG GET USER CBHNDL, %DSU_HTHREAD TO hThread
IF ISTRUE hThread THEN
MSGBOX "Must abort worker thread function " & $CRLF _
& "(or wait for it to complete) to exit", _
%MB_APPLMODAL OR %MB_ICONHAND, "Can't Exit Now"
' Return True To prevent default action (close).
FUNCTION = %TRUE
EXIT FUNCTION
END IF
END IF
CASE %WM_DESTROY
' we should never get here unless it's OK to exit
' kill the timer
KillTimer CBHNDL, %ID_TIMER
CASE %WM_COMMAND
SELECT CASE AS LONG CBCTL
CASE %ID_START
' should not be enabled unless the file in the filename control is available
' create a ready event and save in the designated dialog user value slot
' start a worker thread, passing handle to the dialog
' create a ready event:
hEventReady = m_CreateThreadReadyEvent
' store it where our thread function can read it:
DIALOG SET USER CBHNDL, %DSU_HEVENTREADY, hEVentReady
' get filename and set the pointer; cannot use CONTROL GET TEXT from thread function
' since this thread is in a Wait State
CONTROL GET TEXT CBHNDL, %ID_FILENAME TO sText
DIALOG SET USER CBHNDL, %DSU_FILENAME_PTR, VARPTR(sText)
' create suspended, set priority, and resume:
THREAD CREATE WorkerThreadFunction (CBHNDL) SUSPEND TO hThread
SetThreadPriority hThread, %THREAD_PRIORITY_BELOW_NORMAL
THREAD RESUME hThread TO iRet ' hard to believe the "TO var" is REQUIRED huh?
' wait for the thread function to copy its run parameters:
WaitForSingleObject hEventReady, %INFINITE
' clean up
CloseHandle hEventReady
' Save the (open) thread handle
DIALOG SET USER CBHNDL, %DSU_HTHREAD, hThread
' now that thread function has started, disable the start and enable the abort buttons
CONTROL DISABLE CBHNDL, %ID_START
CONTROL ENABLE CBHNDL, %ID_ABORT
' -------------------------------------------------------------------
' HERE IS THE POINT WHERE YOU WOULD DISABLE/ENABLE *ALL* CONTROLS
' WHOSE ENABLE STATUS DEPENDS IF A WORKER THREAD IS RUNNING OR NOT.
' -------------------------------------------------------------------
CASE %ID_ABORT
' should not be enabled unless a worker thread is running.
DIALOG GET USER CBHNDL, %DSU_HTHREAD TO hTHREAD
CALL AbortWorkerThread (CBHNDL, hThread)
' this will (eventually) post a completion message to this window,
' at which time we close the thread handle
CASE %ID_FILENAME_BROWSE
'FUNCTION OpenFileDialog (BYVAL hWnd AS DWORD, _ ' parent window
' BYVAL sCaption AS STRING, _ ' caption
' sFileSpec AS STRING, _ ' filename
' BYVAL sInitialDir AS STRING, _ ' start directory
' BYVAL sFilter AS STRING, _ ' filename filter
' BYVAL sDefExtension AS STRING, _ ' default extension
' dFlags AS DWORD _ ' flags
' ) AS LONG
sText = ""
IF OpenFileDialog(CBHNDL, "Select File to Process", sText, "", "*.*","", %OFN_FILEMUSTEXIST) THEN
CONTROL SET TEXT CBHNDL, %ID_FILENAME, sText
' this will trigger an EN_CHANGE which will enable the ID_START if it should be enabled
END IF
CASE %ID_FILENAME
' check if the file is avaiable and no worker thread is running already,
' if so enable the start button
IF CBCTLMSG = %EN_CHANGE THEN
DIALOG GET USER CBHNDL, %DSU_HTHREAD TO hThread
IF ISFALSE hThread THEN ' no worker thread is running, so we can enable
' the 'go' button if this file is valid
CONTROL GET TEXT CBHNDL, %ID_FILENAME TO sText
IF DIR$ (sText) > "" THEN
CONTROL ENABLE CBHNDL, %ID_START
ELSE
CONTROL DISABLE CBHNDL, %ID_START
END IF
END IF
END IF
END SELECT
CASE %PWM_THREAD_FUNCTION_COMPLETED
SText = "Worker Thread Function Completed via " & IIF$(CBLPARAM, "Manual Abort", "Natural Causes")
CONTROL SET TEXT CBHNDL, %ID_PROGRESS, Stext
' diasable abort
CONTROL DISABLE CBHNDL, %ID_ABORT
' close the open Thread handle
DIALOG GET USER CBHNDL, %DSU_HTHREAD TO hThread
THREAD CLOSE hThread TO hThread
DIALOG SET USER CBHNDL, %DSU_HTHREAD, %NULL ' set to %NULL so we know no worker thread
' function is currently exectuting.
' enable the start button if we have a valid file in the control
CONTROL GET TEXT CBHNDL, %ID_FILENAME TO sText
IF DIR$ (sText) > "" THEN
CONTROL ENABLE CBHNDL, %ID_START
END IF
' -------------------------------------------------------------------
' HERE IS THE POINT WHERE YOU WOULD DISABLE/ENABLE *ALL* CONTROLS
' WHOSE ENABLE STATUS DEPENDS IF A WORKER THREAD IS RUNNING OR NOT.
' (HERE THREAD FUNCTION IS *NOT* RUNNING).
' -------------------------------------------------------------------
END SELECT
END FUNCTION
' ----------------------------------------------------------
' Worker thread function. Processes the current file by reading it
' %NUMBER_CYCLES times or until manually aborted, then exits after
' posting a private message to the calling Window/Dialog (passed parameter).
' ----------------------------------------------------------
FUNCTION WorkerThreadFunction (BYVAL hDlg AS LONG) AS LONG
LOCAL sFile AS STRING, hFile AS LONG, nRecord AS LONG, Z AS LONG, W AS STRING, S AS STRING
LOCAL sTExt AS STRING, iRecord AS LONG
LOCAL pFilename AS STRING PTR
' ------Wait Variables ------
LOCAL heventReady AS LONG
LOCAL bWaitAll AS LONG
LOCAL iWait AS LONG
'----- event variables ------
LOCAL szEventAbort AS ASCIIZ * 64, hEvent() AS LONG
LOCAL lpSecurity AS SECURITY_ATTRIBUTES PTR
LOCAL bManualReset AS LONG, bInitialState AS LONG
LOCAL bAbort AS LONG
LOCAL SE AS LONG
' ---------------------------------------------------------------
' Get our thread parameters into LOCAL variables before allowing
' the calling thread to continue.
' ----------------------------------------------------------------
DIALOG GET USER hDlg, %DSU_HEVENTREADY TO hEventReady
DIALOG GET USER hDlg, %DSU_FILENAME_PTR TO pFileName
' -----------------------------------------------------------------------------------------------------
' I had to use a string ptr here instead of CONTROL GET TEXT because the calling thread was in a wait
' state (WaitForSingleObject) and apparently (undocumented but not reasonably) CONTROL GET TEXT does
' "something" which must execute in the context of the same thread (suspended) as the dialog.
' Apparently (that means also not documented), DIALOG GET USER does not need to execute anything in the
' context of the thread in which the dialog was created.
' -----------------------------------------------------------------------------------------------------
sFile = @pFileName
' signal the calling thread we have copied our parameters and it may continue
SetEvent hEventReady
' ------------Set up processing events -------------------------------------------------
' We will be looking for two events: an 'abort' event and a 'continue' event.
' WaitForMultipleObjects will suspend this thread until EITHER event is signalled.
' The 'continue' event is created in a signalled state and remains in that state;
' the abort event is created unsignalled, and is signalled by calling SetEvent() at the
' appropriate times.
' We want the abort event to be first (in the array assed to WaitForMultipleObjects), since
' WFMO returns the LOWEST (first) event which is signalled when more than one
' event is signalled.
REDIM hEvent(1) ' we need to monitor two events in total
' Create our abort event as first event in the array
' We use a named event so other parts of the program have access to it.
szEventAbort = $EVENT_ABORT
bManualReset = %TRUE ' The only choice for control freaks. Actually it's immaterial in this program.
lpSecurity = %NULL ' null pointer ==> Default security
bInitialSTate = %FALSe ' create unsignalled.
hEvent(0) = CreateEvent (BYVAL lpSecurity, bManualreset, bInitialState, szEventAbort)
' Create an unnamed event in signalled state as event #2, so worker thread processes forever -
' or at least processes until it completes the task or is manually aborted.
bManualReset = %TRUE ' Do not allow system to reset this to unsignalled
' which it will do if not manual reset type!
bInitialState = %TRUE ' Create this event signalled so wait will be satisfied.
' This event does not need a name: no other functions need access to it!
hEvent(1) = CreateEvent ( BYVAL lpSecurity, bManualReset, bInitialState, BYVAL %NULL)
' set parameter for the wait:
bWaitAll = %FALSE ' we want WFMO to return when EITHER event is signalled (or both).
' --------------------------------------------
' PROCESS THE FILE %NUMBER_CYCLE TIMES
' --------------------------------------------
FOR Z = 1 TO %NUMBER_CYCLES
hFile = FREEFILE
OPEN sFile FOR INPUT AS hFile
FILESCAN hFile, RECORDS TO nRecord
w = USING$ ("Scanned #, Records for cycle # of # ", nRecord, Z, %NUMBER_CYCLES)
CONTROL SET TEXT hDlg, %ID_PROGRESS, W
iRecord = 0& ' record counter for each cycle
' now process each record of the file
DO
' Wait until either the abort event (object 1) or the continue event (object 2) is signalled.
' Since 'continue' is ALWAYS signalled, this function will always return immediately
' and it's just a matter of deciding if Abort was signalled or not.
' iWait will be the LOWEST of the the signalled events when more than one event is signalled.
iWait = WaitForMultipleObjects (BYVAL 2&, BYVAL VARPTR(hEvent(0)), bWaitAll, %INFINITE)
SE = GetLastError
SELECT CASE AS LONG iWait
CASE %WAIT_OBJECT_0 ' first event, the abort, satisfied the waut
bAbort = %TRUE
EXIT DO
CASE %WAIT_OBJECT_0 + 1 ' second event, the "please continue" , satisfied the wait.
IF NOT EOF(hFile) THEN
LINE INPUT #hFile, S
INCR iRecord
IF iRecord MOD %PROGRESS_MODULO = 0 THEN
sText = USING$ ("Processed #, of #, Records in Cycle # of #", iRecord, nrecord, Z, %NUMBER_CYCLES)
CONTROL SET TEXT hDlg, %ID_PROGRESS, sText
END IF
' way too fast to demo unless I code a delay in here (with small file)
SLEEP 5
ELSE ' end of file
' set cool message
sText = USING$ ("All #, records processed in Cycle #", nRecord, Z)
CONTROL SET TEXT hDlg, %ID_PROGRESS, sText
EXIT DO
END IF
CASE ELSE
' needless to say, you should not get here. Ever.
MSGBOX "Oops!" & SystemErrorMessageText (SE)
EXIT DO
END SELECT
LOOP
CLOSE hFile ' done with this cycle
' Go again? or all done?
IF bAbort THEN
EXIT FOR
END IF
NEXT Z ' repeat file processing
' Close our handles to both events since we are done with them
' Since we acquired the handles in this procedure, we should close them here. No, that's not
' any kind of "rule" but it is IMNSHO a "Best Programming Practice"
CloseHandle hEvent(0)
CloseHandle hEvent(1)
' post message to calling window with results; set lparam = the abort status
DIALOG POST hDlg, %PWM_THREAD_FUNCTION_COMPLETED, %NULL, bAbort
END FUNCTION
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 16