Sign in to follow this  
Followers 0
Chance

C/C++ Function CallBack and crash in AutoIt

14 posts in this topic

BOOL ProcessResponce(LPSTR OutBuffer, THREAD_PARAM_COMPONENTS *tData, double Time)
{

// What to do here: need to figure out how to tell this function not to call the callback function
// while another thread is currently already performing a callback, if the two callbacks happen
// I am not entirely sure, but I think this is why AutoIt crashes becausemost of the time this works 
// unless I intentionally call the callback more than once at the same time...


//our callback funtion in autoit
AUTOITCALLBACK *SendResult=((AUTOITCALLBACK*)tData->CallBack);

SendResult(tData->Data, OutBuffer);// this will send some stuff back to autoit


return TRUE;
}

I have about 10-25 threads executing at the same time.

About 4-8 threads will usually do a callback once data is processed, I recently found out that it seems AutoIt can

only support one callback at a time, if my callback function in autoit is called a second time while it's another callback is already running, I get a crash, or just plain old undeclared variable, or those weird errors that say a variable is undeclared and points to a function like "GuiGetMsg()" as the undeclared variable.

This happens only when the AutoIt callback function is called more than once at the same time....

How can I make the threads step through to my callback function one at a time?

All I need are suggestion, this is been bugging me for hours...

Share this post


Link to post
Share on other sites



Ok, someone requested my project, and wrote in a mutex where threads will try to get ownership and execute one by one.

Problem now is that AutoIt will still crash randomly on variable, saying they are undeclared....

This time, after some tests, I am more than sure that my AutoIt callback function is only being called in steps and never at the same time, but it still crashes..

what could be wrong...

All the datatypes for paramaters and return seem to be correct, I'm sending LPWSTR and LPSTR as paramaters.

Global $Temp = DllCallbackRegister("_CallBack", "int", "wstr;str")

typedef void CALLBACK AUTOITCALLBACK(LPVOID, LPVOID);

Share this post


Link to post
Share on other sites

I'm not exactly the world's foremost expert on all things C++ but it seems to me that you have your function returning an int when it should return void? Also, your two parameters are long pointers to void and yet the two parameters in the AutoIt code are different with one being a wide string and the other just a string. I don't know how much water that will hold.

Share this post


Link to post
Share on other sites

I'm not exactly the world's foremost expert on all things C++ but it seems to me that you have your function returning an int when it should return void? Also, your two parameters are long pointers to void and yet the two parameters in the AutoIt code are different with one being a wide string and the other just a string. I don't know how much water that will hold.

I don't know how much water that will hold either!

and I noticed that return value of the "int", I removed it to return nothing, but I'm still getting a random crash.

I'll have to play with this more to see what I get.

Share this post


Link to post
Share on other sites

If you're really controlling access to the code then you shouldn't have problems as far as threads are concerned. However, AutoIt callback can't handle interrupts by design. That means it isn't reentrant. Based on the symptoms I would say your code has issues exactly with that.

Google terms you are not familiar with. I'll be happy to explain further if you get stuck understanding something.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

#6 ·  Posted (edited)

If you're really controlling access to the code then you shouldn't have problems as far as threads are concerned. However, AutoIt callback can't handle interrupts by design. That means it isn't reentrant. Based on the symptoms I would say your code has issues exactly with that.

Google terms you are not familiar with. I'll be happy to explain further if you get stuck understanding something.

You mean like that the AutoIt callback cannot handle being called twice at the same time?

I think that's what you mean. So I tested with message boxes everywhere in my code, and again, it never seems to be called twice at the same time.

I wanted to see if this same behavior happened with code you made, like "resourcesviewerandcompiler.au3", so I put messageboxes all over your callbacks and never got a crash, so there has to be something wrong with my code that me and others aren't seeing.


Edit: Ok, so I just decided to use OnEventMode and something is defiantly wrong...

It's like the AutoIt interpreter is being loaded into another thread and re-running my script, re-creating the GUI that is now being created from the global scope. This is happening exactly when the DLL callback to autoit happens.

It's pretty obvious I'm doing something wrong now.

Here's my AutoIt script:

<removed because obsolete and stuff>

DLL Code:

I'm thinking that somehow, the callback fruntion isn't correctly being initialized/created or something, and it's just completely trying to re-run the entire script and causing the crash.

Kinda found out what's going on now.... look below.

Edited by DeputyDerp

Share this post


Link to post
Share on other sites

#7 ·  Posted (edited)

Funny, I made a few changes to my AutoIt script, and it seems to be working now.

Func CreateThreads()
Local $I
For $x = 0 To 59
$I = Mod($x, 10)
$tURL_COMPONENTS[$x] = DllStructCreate( _; all this is working perfectly in the DLL
"WCHAR UserAgent[1024];" & _
"WCHAR HTTPVerb[1024];" & _
"WCHAR Host[1024];" & _
"WCHAR Resource[1024];" & _
"int Port;" & _
"WCHAR Referer[1024];" & _
"WCHAR Headers[1024];" & _
"ptr ExtraData;" & _
"DWORD Length;" & _
"DWORD TotalLength;" & _
"WCHAR Proxy[1024];" & _
"DWORD ProxyFlags;" & _
"DWORD SendFlags;" & _
"HWND ListView;" & _; unused in the DLL
"ptr CallBack;" _
)

$sOptional = ''
$iOptionalLength = BinaryLen($sOptional) + 5
$tOptional[$x] = DllStructCreate("byte[" & $iOptionalLength & "]")
If $iOptionalLength Then $pOptional[$x] = DllStructGetPtr($tOptional[$x])
DllStructSetData($tOptional[$x], 1, $sOptional)

DllStructSetData($tURL_COMPONENTS[$x], 1, "(Compatable; AutoIt/v3 WinHTTP Test Script)")
DllStructSetData($tURL_COMPONENTS[$x], 2, "GET")
DllStructSetData($tURL_COMPONENTS[$x], 3, "unrealx.lt")
DllStructSetData($tURL_COMPONENTS[$x], 4, "showip.php")
DllStructSetData($tURL_COMPONENTS[$x], 5, $INTERNET_DEFAULT_HTTP_PORT)
DllStructSetData($tURL_COMPONENTS[$x], 6, '')
DllStructSetData($tURL_COMPONENTS[$x], 7, $ContentType)
DllStructSetData($tURL_COMPONENTS[$x], 8, $pOptional[$x])
DllStructSetData($tURL_COMPONENTS[$x], 9, $iOptionalLength)
DllStructSetData($tURL_COMPONENTS[$x], 10, $iOptionalLength)
DllStructSetData($tURL_COMPONENTS[$x], 11, $Proxies[$i]); Cycle through proxies for this test
DllStructSetData($tURL_COMPONENTS[$x], 12, $WINHTTP_ACCESS_TYPE_NAMED_PROXY)
DllStructSetData($tURL_COMPONENTS[$x], 13, $WINHTTP_FLAG_ESCAPE_DISABLE);WINHTTP_FLAG_SECURE
DllStructSetData($tURL_COMPONENTS[$x], 14, GUICtrlGetHandle($hListView))


DllStructSetData($tURL_COMPONENTS[$x], 15, $pCallback); set a pointer to the CallBack function

$aThread[$x] = _AutoItThreadCreate(DllStructGetPtr($tURL_COMPONENTS[$x])); Create the thread!

;$tURL_COMPONENTS = 0

Next
EndFunc ;==>CreateThreads

Func _AutoItCallBack($test, $data)

Local $Ubound = UBound($ResultsArray); get array size
ReDim $ResultsArray[$Ubound + 1][2]; redim it
$ResultsArray[$Ubound - 1][0] = $test; add data returned from thread
$ResultsArray[$Ubound - 1][1] = $data
AdlibRegister("_AddListViewItem", 5000); this will add the resulting array to the list view after no activity for 5 seconds

Return
EndFunc ;==>_AutoItCallBack

Now I'm creating up to 60 threads and getting like 30 callbacks on average and crashes way less often...

It looks like somehow, not overwriting my structures and other variables in the AutoIt side seem to have allowed the dll to do its job and callback correctly.

This is how I create the Autot callback.

//...
     typedef LPVOID CALLBACK AUTOITCALLBACK(LPVOID, LPVOID);
     AUTOITCALLBACK* SendResult=((AUTOITCALLBACK*)tData->CallBack);
     SendResult(tData->Proxy, pszOutBuffer);
//...

I think it might have something to do with this, because I've never seen a callback function recreate my GUI 2-3 times over whenever the callback is called and a messagebox is in it...

Edited by DeputyDerp

Share this post


Link to post
Share on other sites

...As you already saw creating GUI conrols from other threads does not seem to be very good choice.


♡♡♡

.

eMyvnE

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

Why do you have this line:

Sleep(999999)

What happens if you do "normal":

While 1
Sleep(10)
WEnd

I usually do that in scripts that use oneventmode or the plain old GUIRegisterMsg method. I don't remember where I picked that up from...

but odly enough, it solved one problem, in the example I posted first with the script and dll code, using the change you mention, the gui isn't being created multiple times anymore.

Before removing the sleep and adding the while statment, I was playing with it for a good while and I even got it to create like 4 guis, autoits main thread seemd to have rerun the gui creation part every once in a while for some reason....

I have no idea what was happening so I started to think it's maybe the way I'm initializing the callback in the dll.

..As you already saw creating GUI conrols from other threads does not seem to be very good choice.

I noticed :P Edited by DeputyDerp

Share this post


Link to post
Share on other sites

#11 ·  Posted (edited)

Ok, so new years passed and I had time again to get back on this.

I know what's wrong now, or at least more or less know the problem causing the problem is.

#include <Windows.h>
typedef LPVOID CALLBACK AUTOITCALLBACK(LPWSTR, LPSTR, int);

DWORD WINAPI DoCallBack(LPVOID threadData)
{
    AUTOITCALLBACK *SendResult((AUTOITCALLBACK*)threadData);
    int i;
    for (i = 0; i < 1000; i++)
    {
        Sleep(10);
        SendResult(L"LPWSTR", "LPSTR", i);
    }
    return 1;
} 

HANDLE WINAPI DoCallBackThread(LPVOID threadData) 
{
    HANDLE h = CreateThread(NULL, 0, &DoCallBack, threadData, 0, NULL);
    Sleep(1000);
    return h;
}

DoCallBack = Exported function, safely calls my callback no matter how many times I call it, even with message boxes in the callback, everything is ok.

DoCallBackThread = exported function that creates a thread and calls DoCallBack, this will eventually crash autoit.

But I don't get why this happens!

Is this because autoit's not thread safe? if that's why than this really sucks.

billions and billions of thread work flawlessly unless I make one of the threads do a callback to autoit, so why is doing a callback from another thread so special it crashes autoit?

I figured that this should work because guiregistermsg basically does the same, it interrupts auoit to do some things like in an instant, and so does my style of callbacks from another thread.

So obviously it's not and this will not work huh...

Edited by DeputyDerp

Share this post


Link to post
Share on other sites

It's worth noting that AutoIt was not designed to be thread friendly.

Share this post


Link to post
Share on other sites

I don't at all understand this line:

AUTOITCALLBACK *SendResult((AUTOITCALLBACK*)threadData);

So threadData, an LPVOID, is cast to a pointer to a typedef for an LPVOID, then this result is passed to SendResult? Or is that the functional notation for casts and if so then you're assigning the function pointer of threadData to SendResult? WHich is now an alias for threadData?

Share this post


Link to post
Share on other sites

#14 ·  Posted (edited)

I don't at all understand this line:

AUTOITCALLBACK *SendResult((AUTOITCALLBACK*)threadData);

So threadData, an LPVOID, is cast to a pointer to a typedef for an LPVOID, then this result is passed to SendResult? Or is that the functional notation for casts and if so then you're assigning the function pointer of threadData to SendResult? WHich is now an alias for threadData?

Yes. That's more or less what's happening.

I removed that part of my code and opted for use of mailslots, which I think it kinda like named pipes but less complicated.

It's worth noting that AutoIt was not designed to be thread friendly.

I've noticed.

using mailslots, I'm not getting as many crashes. But an occasional access violation happens once in a while now.

I've been getting emails from some of the developers helping me, and I guess I'm the only one who it's crashing on...

this is weird, maybe it's a system specific thing.

I'll have to wait to hear from the people who are better at the c/c++ code as I'm mostly taking care of the AutoIt code since it's what I'm better at.

But at least 3 people have reported running the application for about 3 hours with no problems. related to crashes, other than a small bug in the Do while loop that causes excesseve CPU usage.

Anyway, long story short is that I guess doing callbacks from other threads is very buggy and not recommended, me and a few people cant see what waa wrong with the code so we will assume it's a lack of understanding the internal autoit callback stuff, so remote thread callbacks are a no no as it seems.

Edited by DeputyDerp

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0