DLL with callback problem (1 Viewer)

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
I have made a simple dll with C for use in an Ms Access vba project. The dll makes a new thread and makes use of a vba callback function inside the thread's code. According to MS documentation the vba callback function has to be in a vba module and not in a form's code. Inside the callback function I set a form's filter property. But because of this the Access crashes when the execution it arrives there!!! Why? Other references to form's components have no problem (for example when I set the value of the form's textbox)
The mysterious is that I found the problem exists only when the vba Callback Sub is called from the Thread function (ThreadFunc). If it is called from within dll main thread then form's filter is set ok without crash (!!!)

Thanks

The C dll code compiled with VS 2017:
Code:
#include <windows.h>
#include <stdlib.h>
#include <tchar.h> 

typedef void(__stdcall * CallbackFunction)();

DWORD __stdcall ThreadFunc(CallbackFunction Callback) { 
    Callback();
    return 0;
}
 
__declspec(dllexport) void __stdcall message(CallbackFunction Callback) {
     // Array to store thread handles
    HANDLE Array_Of_Thread_Handles[1];
    Array_Of_Thread_Handles[0] = CreateThread(NULL, 0, ThreadFunc, Callback, 0, NULL);
}
And here is the code that call the dll function "message" from within vba
Code:
Private Sub Form_Load()
    Call message(AddressOf Callback)
End Sub
The vba Declaration of dll
Code:
 Public Declare Sub message Lib "test.dll" Alias "_message@4" (ByVal Callback As Long)
The vba callback subroutine (resides on a Module according to MS documentation)
Code:
 Public Sub Callback()
    On Error Resume Next
    Forms("MyForm").txtTest="test" 'this line executed well and the string "test" appears ok in the form.
    Forms("MyForm").Filter="" 'in this line Access crashes out
    Forms("MyForm").FilterOn=True
End Sub
 
Last edited:

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
First, I will confirm marlan's memory. Since VBA is pseudo-compiled and the VBA environment interprets the pseudo-code, multi-threading is not possible within VBA. There is only one VBA interpreter and it is, to the best of my knowledge, not coded re-entrantly to allow for multiple threading. Note that this is a different statement than saying that ACCESS cannot deal with multiple threads of its own; it surely can do so. It is that VBA and Access SQL via JET or ACE, being interpreted, do not support true parallel operation. Further, since JET & ACE are called synchronously, native Access + JET/ACE is ALSO not truly parallel even though they occupy two processes. A pass-thru or remote procedure call operation to an active SQL back-end is the only way that Access does things in parallel from the user viewpoint. Everything else, from our perspective, is serialized.

However, since the inner workings of Access are pretty much a "black box" to us, it is very hard to say what would happen if a self-contained, self-terminating thread were to be created, or if you arranged for ANOTHER call that would rejoin that thread and dissolve it at a later time.

Second, I am not sure about the issue of directly setting the .Filter that way. Not that you cannot directly load .Filter and .FilterOn properties, but from that context I am not sure about threading as a way to get there. I had to pull out my Win32 API reference books for this one.

I think an experiment might pinpoint the problem. Change the order of filling the text box and the filter. Make the .Filter and .FilterOn be the first things you set and see which line crashes. If, as I suspect, it is the 2nd line to touch a property, then you will set the .Filter but fail to set .FilterOn, and if so, that means you can't do this in a thread.

When I read the discussion about the thread created this way, my question (for my own consideration) is that you are using 0 for the fdwCreate argument, which means the thread gets executed immediately. In the code you have specified, there is no thread self-termination so you would leave a dangling thread.

What bothers me most is that this is being triggered from a form event routine, Form_Load. Form events declared via "Private Sub" syntax are not like true interrupt events; they are synchronous. However, the description of CreateThread suggests that there are limits of what can happen when a thread is created. I don't think that you are running into the "serialization of DLL initiation" problem because by that time, the other DLL code should be ready. But since this code has to be in the address space, it is subject to the limitations of having only one thread active at any time within the process. I'm thinking that somewhere along the way, the VBA interpreter finds your CallBack routine but on the attempt to return, it gets lost. That is because your code flow leaves the Access environment then enters that environment again but with that "foreign" call still on the stack.

Tell us in ENGLISH with NO references to code: What were you trying to do? Because there is no reason you couldn't have done a filter modification in ordinary VBA without resorting to C++ code. So I suspect this was an experiment to learn how to write your own DLL code. But the callback part, calling back into the VBA environment, is the tricky part. I don't think that works. At least, if it does, I have never seen it before. I can only say I think this fails because of attempting to re-enter an interpreted environment in a non-standard way.

Third, we have a reason for not liking it when folks cross-post to multiple forums without stating that they are doing so. And you got a valid response on UA regarding the lack of tools to allow a shared resource to be controlled in such a way as to prevent it from two threads "stepping on something" that results in "destructive interference." It is valid to consider that when you attempt to set the filter you will have an automatic reaction from MSACCESS.EXE to do its "behind the scenes" work to modify the bound form's recordset (which is where the filter gets copied when you supply it) - and therein lies the problem. THAT part of Access might WELL be in its own thread and vying for access to the form properties.

We don't blacklist you or anything like that when you cross-post without revealing it - but we do consider it rude and potentially wasteful of our time when you ask a question that already has reasonable answers. "Shopping around" for a better answer is easily understandable. In the future, if you feel that you are getting nowhere on one forum, we know you might wish to use another forum - but it is considered polite to note that fact and very impolite not to do so.
 
Last edited:

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
Now, a second post with a different thrust, because you SHOULD have more info that would be crucial. If Access really crashes, it is not in a vacuum. Even WinXP does serious event logging. Run your experiment, and when Access crashes, note the time on the little date/time display in the far right end of the Windows Task Bar.

From your desktop, get to Control Panel >> Administrative Tools >> Event Viewer. You will have System and Security logs and might also have Application logs. Heck, depending on your anti-virus, you might even have something there. One by one, visit each log file with Event Viewer.

The events are sorted by date and time so it should be trivial to find anything in each log from that time involving MSACCESS.EXE as the failing process. You would be looking for the final status code and anything else it tries to tell you about the events of the crash. That might be instructive.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
below I have the report from Event Viewer


Faulting application name: MSACCESS.EXE, version: 14.0.7230.5000, time stamp: 0x5c6738e8
Faulting module name: MSACCESS.EXE, version: 14.0.7230.5000, time stamp: 0x5c6738e8
Exception code: 0xc0000005
Fault offset: 0x0015a3f1
Faulting process id: 0x66fc
Faulting application start time: 0x01d5161113939034
Faulting application path: C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.EXE
Faulting module path: C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.EXE
Report Id: 665b2a08-8204-11e9-862a-64315042aab9
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
more information:
1. I have switched the textbox reference with filter. So first to execute the filter and second the textbox. The result was that filter executed but not textbox value set.
2. I removed textbox and filter's settings. I wrote only one line of code: MyForm.RecordSource="...". Access crashes out!!!


I need to monitoring a text file for changes. And I used some Windows API in dll like FindFirstChangeNotification. Based on these changes I have to filter the form using a vba callback that is called form within dll thread function. The problem is that everything is ok (I open and read the txt, I log the contents of txt in the SQL Server backend database using ADO but when I set some form's properties Access crashes out)


If I can't use threading in Access even with a dll then I have to leave MS Access when I need to make more serious applications. And I think that after this disappointment I will leave MS Access forever.
But I have a question. Are there Windows API dll functions that use internally threading with callback? If yes would be nice to use one with the same way to see how it is work. I know that there are API functions with callback (for example EnumWindows) but I do not know with threading inside DLL.
 
Last edited:

sonic8

AWF VIP
Local time
Today, 21:00
Joined
Oct 27, 2015
Messages
998
If I can't use threading in Access even with a dll then I have to leave MS Access when I need to make more serious applications.
Access is not the proper tool if you want to create an application with serious multi threading.
In any case, you need to understand threading on a deep level before creating an application with serious multi threading in any development environment.


Are there Windows API dll functions that use internally threading with callback?
I assume most Windows API functions are designed with great care to not invoke a callback from a different thread, because of the problems this might cause.


However, the obvious prime example of an Windows API functions that will invoke a callback on a different thread is CreateThread.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
I assume most Windows API functions are designed with great care to not invoke a callback from a different thread, because of the problems this might cause.


Ok... This means that if we could call the Callback from the main thread of dll (ie from dll message function) would be perfect. Indeed I have tried it and works perfect. But we must have a mechanism in the dll that thread function notify the main dll thread when it has to inform for a change. The problem also is that that "message" dll function runs in the same thread as vba's!!!
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
If we create a C callback inside dll and the thread function call this (instead of vba callback) and then the dll callback call vba callback?
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
Exception code: 0xc0000005
...
Faulting module path: C:\Program Files (x86)\Microsoft Office\Office14\MSACCESS.EXE

As I suspected, you got a type of error common to mis-use of addresses. In this code, the "c" says that it is a severe error. (Translation: Unrecoverable.) The "5" says it is a memory access violation.

First, remember that in windows your process is VIRTUAL. It has addresses that are mapped to physical memory by hardware memory relocation registers. Under Windows ever since Windows NT came out, NO process ever directly touches a physical memory address once the boot sequence is finished. Even the O/S uses virtual mapping once the startup code terminates. Therefore, when you try to touch something in memory, it MUST be in a mapped area.

There are ways to make certain specific memory areas accessible but you have to use system API calls to do it. If you have Win7 or Win10, the O/S has a feature called code protection that uses the memory management hardware to prevent rogue code from overwriting any executable code. The code authors have a way to set the code's memory usage tags so that the image load process includes setting memory access codes appropriately.

A memory access violation occurs in one of only a couple of ways. Error 5 says that EITHER you attempted to refer to an address for which no physical mapping is defined (i.e. no physical memory, i.e. the hardware memory fetch circuit fails to develop a full physical address) OR you attempted to write to an address that was mapped as READ-ONLY (i.e. modify protected code, i.e. the hardware memory write circuit encounters a memory page mapped with the NOWRITE flag set.)

This is a kernel-level hardware fault - a true hardware interrupt as opposed to ordinary Access events caused by a purely normal software action like opening a form. It is unlikely that you would be easily able to intercept this fault with a trap handler because the faulting module is within MSACCESS.EXE, not your .DLL file. A trap handler only has visibility to your code, not to the Access executable code.

I have switched the textbox reference with filter. So first to execute the filter and second the textbox. The result was that filter executed but not textbox value set.

As I predicted, the problem stayed as the second line (not counting the ON ERROR line) which means that the VBA interpreter got lost after the first executable line that tried to touch the external object. The first touch worked, but the next touch did not.

You cannot use threading like this because the Access environment isn't oriented towards user-level threading. You need to write your whole application in something that uses "true" compilation as opposed to VBA's pseudo-code compilation.

I need to monitoring a text file for changes.

If it is really just a text file then monitor its date of last update. When it changes, you know to take a closer look at it. If you need a real-time monitor of text file changes, though, I don't know where you are going to find support. I'm not even sure how I would approach this problem with OpenVMS and that is considered "mainframe" these days.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
If it is really just a text file then monitor its date of last update. When it changes, you know to take a closer look at it. If you need a real-time monitor of text file changes, though, I don't know where you are going to find support.


I need a real time solution and not polling... so I leave behind Access and I change platform from now on...
The problem is that the application is too huge and works for years to develop again from scratch only for this reason!!!


Thanks anyway
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
Here is your reality. The file system isn't really set up to support real-time file update monitoring. The only way I know that you COULD do this with Windows would be to write a driver mod that would trap a write operation to a particular device/file combo and somehow trigger some watchdog process. If we are dealing with a case of Network Attached Storage, I'm not even sure which driver you would need to modify to support that interception.

You should also note that most security code will stop you from that approach because that is the behavior of virus-class malware. Windows Defender should stop you cold if you try to do this. I know Norton and Kaspersky would stop you. Don't have as much experience with other anti-virals but I'll bet they would also complain.

Windows won't tell you what you want to know. You are inventing an ability that Microsoft chose to not implement. I am not saying you can't because I told you the only approach that has half a chance, basically a driver "hook" modification. But you are clearly overreaching the abilities of the "standard" tools defined under Windows.

I wish you luck, and if you wish to try another platform, some of the Open UNIX variants give you source code, even at driver level, that you could modify to try to intercept file actions. However, in their infinite (?) wisdom, Microsoft has not seen fit to publish a lot of their code. Oh, one more thing. Doing what you are trying to do WILL in all probability violate the conditions of your software warranty AND any support contract you might have.
 

sonic8

AWF VIP
Local time
Today, 21:00
Joined
Oct 27, 2015
Messages
998
@nonlinearly: I'm getting slightly annoyed. After starting two threads in different forums simultaneously, it appears you are not even reading the replies in the other forum anymore.
I need a real time solution and not polling... so I leave behind Access and I change platform from now on...
The problem is that the application is too huge and works for years to develop again from scratch only for this reason!!!


It is your responsibility as developer to handle the syncing of the threads to update the UI. This is not an Access specific issue. Access reacts very badly to ignoring this by crashing, but it will cause problems in all environments. - Leaving Access just for this reason is not a sensible decision, as you yourself recognized already.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
....The file system isn't really set up to support real-time file update monitoring. .....

You are inventing an ability that Microsoft chose to not implement. .... But you are clearly overreaching the abilities of the "standard" tools defined under Windows.

...However, in their infinite (?) wisdom, Microsoft has not seen fit to publish a lot of their code. ...




This is wrong... I have already code from Microsoft that do this... monitoring a Windows folder for changes!!! And works like a charm for other languages like C#... !!! The problem is not Windows but Access.
Monitoring folder's changes in Windows is used quite often and there is code for all languages. There is code for C#, C++,Java etc. So I do not understand yours last comments!
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
@nonlinearly: I'm getting slightly annoyed. After starting two threads in different forums simultaneously, it appears you are not even reading the replies in the other forum anymore.
Don't be so rushed


It is your responsibility as developer to handle the syncing of the threads to update the UI.

I'm not asking people just to confirm my question


This is not an Access specific issue. Access reacts very badly to ignoring this by crashing, but it will cause problems in all environments. - Leaving Access just for this reason is not a sensible decision, as you yourself recognized already.
Just read my previous response


Thanks anyway
 

sonic8

AWF VIP
Local time
Today, 21:00
Joined
Oct 27, 2015
Messages
998
Just read my previous response
I did already. - I don't see how it is relevant to my comment.


Sure, there are components available for other languages, like the FileSystemWatcher for .Net, which already includes the synchronization of its worker threads back to the UI thread. If you use one of those, I'm quite confident that you will not experience Access crashing when handling its events.


If you don't use such a component, it's your responsibility to take care of synchronization. - If this is just "confirming your question", then you should now act upon this confirmation.
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
OK, I stand corrected - but the research that I did, including going deep into my Win32API books, didn't show me anything useful. You found something that I could not find, probably because you and I asked Google different questions. But I stand on the comment that what you want is unusual enough that knowledge of it is uncommon.

My comments on VBA being pseudo-compiled and then interpreted are your answer to why you have trouble in that environment. And I did suggest that truly compiled languages might be better suited to what you wanted to do. Which seems consistent with your remarks in post #14.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
OK, I stand corrected - but the research that I did, including going deep into my Win32API books, didn't show me anything useful. You found something that I could not find, probably because you and I asked Google different questions. But I stand on the comment that what you want is unusual enough that knowledge of it is uncommon.
A simple example from Microsoft ready to be compiled and run:

https://docs.microsoft.com/en-us/windows/desktop/fileio/obtaining-directory-change-notifications
 

The_Doc_Man

Immoderate Moderator
Staff member
Local time
Today, 15:00
Joined
Feb 28, 2001
Messages
27,001
OK, researched it a bit more. Thanks for the link. And for the record, this doesn't tell you that an arbitrary file was changed. It tells you that a directory was changed and you have to work a bit more to work out what file in that directory actually changed. But it is an interesting technique.

Technically, this notifies you of a change if and only if the program making the change closes the file, in which case you could set up your "wait handle" to wait for FILE_NOTIFY_CHANGE_LAST_WRITE (change last write date on any file). So that means it is only going to tell you about this change after the fact. If there is a delay, you don't find out right away until the change is actually committed.

Of course, since you can't interfere with the file while another program has it open, that is about as close to "real-time" as you are going to get. But let me ask this: Once you know that file X has been updated, how much time do you have before you must start working on it? Milliseconds? Seconds? Tens of seconds?

Because it seems to me that if you were willing to go to this trouble to write C++ code, you could have built a C++ main program that waits for the file and when appropriately triggered, it launches something. Which COULD be an instance of your database with a Macro (using command-line X:macro-name) to run the code to process your file once it has been updated. Having Access wait for something is not what Access wants to do, but having a stub to launch Access might be just as good if you don't mind the implied slower response of a couple of seconds before it responds.
 

nonlinearly

Registered User.
Local time
Today, 13:00
Joined
Jun 13, 2012
Messages
21
... this doesn't tell you that an arbitrary file was changed. It tells you that a directory was changed and you have to work a bit more to work out what file in that directory actually changed. But it is an interesting technique.
The directory has only one text file. Τhe file we are interested in.
Technically, this notifies you of a change if and only if the program making the change closes the file, in which case you could set up your "wait handle" to wait for FILE_NOTIFY_CHANGE_LAST_WRITE (change last write date on any file).
The link is an example. It doesn't mean that I use it as is. Of course I have changed the wait handle to FILE_NOTIFY_CHANGE_LAST_WRITE.
So that means it is only going to tell you about this change after the fact. If there is a delay, you don't find out right away until the change is actually committed.Of course, since you can't interfere with the file while another program has it open, that is about as close to "real-time" as you are going to get.
There is no delay. The program that changes the contents of the text file close the file immediately after the changes.
But let me ask this: Once you know that file X has been updated, how much time do you have before you must start working on it? Milliseconds? Seconds? Tens of seconds?
zero time... or in the worst case Milliseconds... or as soon as possible
Because it seems to me that if you were willing to go to this trouble to write C++ code, you could have built a C++ main program that waits for the file and when appropriately triggered, it launches something. Which COULD be an instance of your database with a Macro (using command-line X:macro-name) to run the code to process your file once it has been updated. Having Access wait for something is not what Access wants to do, but having a stub to launch Access might be just as good if you don't mind the implied slower response of a couple of seconds before it responds.
This has nothing to do with our business requirements. We can not change the way we do business due to the inability of software to face a situation.

Thanks for your analysis but has nothing (or little) to do with the problem I have already described. The problem is how can I use this code to a separate thread with a callback to vba that uses UI elements of MS Access Form without crash. I had just removed the details not to mislead someone.

I think that the only way is to make a COM component that is more robust when we have to deal with communication between different applications that maybe have been written in different technologies (like mine) but I don't know how to make a COM component.
 
Last edited:

Users who are viewing this thread

Top Bottom