Any application host event supported for MS Access?

Add-in Express™ Support Service
That's what is more important than anything else

Any application host event supported for MS Access?
Don't see any in the documentation 
bobcalco




Posts: 66
Joined: 2019-03-20
Just acquired Add-Ins Express for VCL to integrate our Delphi app deeply with Office. Although I'm also interested in Excel and a few other Office apps, the one I'm most interested in extending, given the nature of our application, is MS Access.

Perusing the documentation I see there are app events for every Office application under the sun -- except one: Access. I am wondering why this is?

I mean - who uses Front Page? Or Map Point? But Access is used all over the place. So, this (apparent?) omission surprises me.

I am very interested to respond to Access host application events and am wondering if there is some technical limitation preventing this or if I'm the first person to need it?
Posted 21 Mar, 2019 07:51:26 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello,

Access doesn't provide application-level events.


Andrei Smolin
Add-in Express Team Leader
Posted 21 Mar, 2019 08:15:32 Top
bobcalco




Posts: 66
Joined: 2019-03-20
Access doesn't provide application-level events.


Then why does the .NET version support Access application events?

See: https://www.add-in-express.com/creating-addins-blog/2010/09/27/office-access-2010-addins/

User added an image
Posted 21 Mar, 2019 08:21:41 Top
bobcalco




Posts: 66
Joined: 2019-03-20
Perhaps a better question then is - what can I do with a COM add-in in Access? I'm interested, for example, when a user updates a table definition. I'd like to add a panel for additional categories of properties for a table column that I maintain.
Posted 21 Mar, 2019 08:24:32 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
I remember only one COM add-in for Access; see https://archive.codeplex.com/?p=accesspowertools. You can check if a recordset connected to the table(s) containing table definitions produces enough info for you. Sorry, I cannot suggest anything else. I don't think Access allows solving this task.


Andrei Smolin
Add-in Express Team Leader
Posted 21 Mar, 2019 08:53:04 Top
bobcalco




Posts: 66
Joined: 2019-03-20
That product refers to your add-in express for .NET, which appears to support Access events somehow:

private void adxAccessEvents_CurrentObjectChanged(object sender, string name,
    AddinExpress.MSO.ADXAcObjectType objType, AddinExpress.MSO.ADXAcObjectState objState)
{
    switch (name.ToUpper())
    {
        case "EMPLOYEE DETAILS":
            btnEmployeeLeave.Enabled = true;
            break;
 
        case "SHIPPERDETAILS":
            btnShipperRatesTaxes.Enabled = true;
            break;
 
        default:
            btnEmployeeLeave.Enabled = false;
            btnShipperRatesTaxes.Enabled = false;
            break;
    }
}


(The above code snippet is from the link I mentioned previously, above.)

However, no such event is hookable using the VCL version. Why?
Posted 21 Mar, 2019 09:47:57 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Looking into this. Will let you know my result tomorrow as now is the end of the business day here. See you tomorrow.


Andrei Smolin
Add-in Express Team Leader
Posted 21 Mar, 2019 10:16:53 Top
bobcalco




Posts: 66
Joined: 2019-03-20
Thanks, Andrei!
Posted 21 Mar, 2019 10:24:37 Top
Andrei Smolin


Add-in Express team


Posts: 18821
Joined: 2006-05-11
Hello,

Absolutely so, Access doesn't provide any application-level events; you can check the type library.

The events you see are raised by a custom code that uses a timer to check what's going on in the Access application. Below is the method that handles the Tick event of that timer; the timer runs on the main thread. To translate that method (C#) to Pascal, use the following pointers:
- applicationObject is a reference to the Application object from the Access object model.
- {some object}.GetType().InvokeMember({member name},,,{the same object},{array of parameters or null}) is a late binding call: {some object}.{member name}({parameters}).
- Ignore the Install() method call as well as everything related to commandbars and to the adxComponents collection.
- Marshal.ReleaseComObject(currentProject) is equivalent to currentProject := nil.
- AddinExpress.MSO.ADXAccessAppEvents is the class (component) providing Access events.

Hope this helps.


Andrei Smolin
Add-in Express Team Leader


private bool dbLoaded = false;
private string dbFullName = "";
private bool timerLocked = false;
private string currentObjectName = "";
private ADXAcObjectType currentObjectType = ADXAcObjectType.adxAcDefault;
private ADXAcObjectState currentObjectState = ADXAcObjectState.acObjStateDefault;

private void AccessTick(object sender, System.EventArgs e)
{
    if (!timerLocked)
    {
        timerLocked = true;
        object currentProject = applicationObject.GetType().InvokeMember("CurrentProject", BindingFlags.GetProperty, null, applicationObject, null);
        if (currentProject != null)
        {
            string dbName = "";
            bool isConnected = false;
            try
            {
                isConnected = Convert.ToBoolean(currentProject.GetType().InvokeMember("IsConnected", BindingFlags.GetProperty, null, currentProject, null));
                dbName = Convert.ToString(currentProject.GetType().InvokeMember("FullName", BindingFlags.GetProperty, null, currentProject, null));
            }
            catch
            {
            }
            if (isConnected && (!String.IsNullOrEmpty(dbName)))
            {
                if (dbLoaded && dbFullName != dbName)
                {
                    if (evtByName[ADXConstants.adxAccessApplicationEventsClassName] is AddinExpress.MSO.ADXAccessAppEvents)
                        (evtByName[ADXConstants.adxAccessApplicationEventsClassName] as AddinExpress.MSO.ADXAccessAppEvents).DoCloseDatabase();
                    ClearAccessCommandBarsInterfaces();
                }
                if (!dbLoaded || dbFullName != dbName)
                {
                    dbLoaded = true;
                    if (adxComponents != null)
                    {
                        IComponent component;
                        for (int c = 0; c < adxComponents.Components.Count; c++)
                        {
                            component = adxComponents.Components[c];
                            if (component is AddinExpress.MSO.ADXAccessAppEvents)
                            {
                                (component as AddinExpress.MSO.ADXAccessAppEvents).AddinModule = this;
                                evtByName[(component as AddinExpress.MSO.ADXAccessAppEvents).ClassName] = component;
                                break;
                            }
                        }
                    }
                    if (evtByName[ADXConstants.adxAccessApplicationEventsClassName] is AddinExpress.MSO.ADXAccessAppEvents)
                        (evtByName[ADXConstants.adxAccessApplicationEventsClassName] as AddinExpress.MSO.ADXAccessAppEvents).DoOpenDatabase();
                    dbFullName = dbName;
                    try
                    {
                        Install();
                    }
                    catch
                    {
                    }
                }
                else
                {
                    string objectName = null; ADXAcObjectType objectType = ADXAcObjectType.adxAcDefault;
                    ADXAcObjectState objState = ADXAcObjectState.acObjStateDefault;
                    try
                    {
                        objectName = Convert.ToString(applicationObject.GetType().InvokeMember("CurrentObjectName", BindingFlags.GetProperty, null, applicationObject, null));
                        objectType = (ADXAcObjectType)Convert.ToInt32(applicationObject.GetType().InvokeMember("CurrentObjectType", BindingFlags.GetProperty, null, applicationObject, null));
                        if (objectName != null && objectName != String.Empty)
                        {
                            objState = (ADXAcObjectState)Convert.ToInt32(applicationObject.GetType().InvokeMember("SysCmd", BindingFlags.InvokeMethod, null, applicationObject,
                                new object[] { 10, objectType, objectName }));
                        }
                    }
                    catch
                    {
                    }
                    if (objectName != currentObjectName || objectType != currentObjectType || objState != currentObjectState)
                    {
                        currentObjectName = objectName; currentObjectType = objectType; currentObjectState = objState;
                        if (evtByName[ADXConstants.adxAccessApplicationEventsClassName] is AddinExpress.MSO.ADXAccessAppEvents)
                            (evtByName[ADXConstants.adxAccessApplicationEventsClassName] as AddinExpress.MSO.ADXAccessAppEvents).
                                DoCurrentObjectChanged(currentObjectName, currentObjectType, currentObjectState);
                    }
                    else
                    {
                        if (evtByName[ADXConstants.adxAccessApplicationEventsClassName] is AddinExpress.MSO.ADXAccessAppEvents)
                            (evtByName[ADXConstants.adxAccessApplicationEventsClassName] as AddinExpress.MSO.ADXAccessAppEvents).DoAccessIdle();
                    }
                }
            }
            else if (dbLoaded)
            {
                dbLoaded = false; dbFullName = dbName;
                if (evtByName[ADXConstants.adxAccessApplicationEventsClassName] is AddinExpress.MSO.ADXAccessAppEvents)
                    (evtByName[ADXConstants.adxAccessApplicationEventsClassName] as AddinExpress.MSO.ADXAccessAppEvents).DoCloseDatabase();
                ClearAccessCommandBarsInterfaces();
            }
            Marshal.ReleaseComObject(currentProject);
        }
        timerLocked = false;
    }
}
Posted 22 Mar, 2019 06:57:53 Top
bobcalco




Posts: 66
Joined: 2019-03-20
It would be great if this feature was added more or less urgently to the VCL version of the product. I bought the Premium edition fully confident the apparent capabilities related to Access in the advertising related to the .NET flavor of the product would be available in the VCL version (there was no caveat or obvious reason not to assume it). I will implement the above approach but this new development requirement kind of negates some of the acceleration I expected your product to give me with regards to Access integration. (Excel integration looks promising though.) Thanks for getting back to me promptly in any case.
Posted 22 Mar, 2019 08:55:35 Top