VB.NET talking with Microsoft Access - can one do callbacks? - vb.net

I'm building an add-on UI system in VB.NET (Visual Basic specifically) that uses data from a legacy Microsoft Access system (that will at least for some time needs to be useful via its old, internal UI). I can do pretty much everything I need to do from VB.net - work with the data tables, call Access functions that contain decent functionality, etc.
What I'm trying to figure out is if Microsoft Access can call back to VB.NET (e.g., perhaps allowing me to pass in a function reference to be invoked).
The specific motivation is that there are 2 or 3 time-consuming Microsoft Access functions that produce critical output data - if you were invoking them directly via the MS Access interface, you would see things like progress bars and the occasional text update on a form of the work phase, progress %, etc. I can modify the MS Access code not to do these things if called from VB.net, but I'd really rather have the MS Access code reach back into VB.net so that I can provide some progress feedback in the new interface.
An elegant solution would be, as alluded to above, have VB.NET pass in the function delegate for MS Access to call - but as these are entirely different memory spaces, etc., I would think some internal framework would be required to get such a thing to work - perhaps via a shared DLL in some manner. I could go the old "polling" route, wherein MS Access writes out status to a table, and the (asynchronous) VB.NET app polls that table and uses what's there. That seems like such an inelegant hack, unnecessarily wasteful in resources and potentially bug prone, that I hate to go that route. If I could build just one function that MS Access could use, something that just passes a status string back, I think that'd cover the vast majority of cases.
Any suggestions would be most welcome.

I often create COM objects in .net for use with Access. Thus in your access code you could MOST certainly have the process loop “call” the .net object to update the process.
However, you likely are automating a copy of Access, and thus would like Access to call a routine in the EXISTING automation client, not have an instance of Access call a custom .net object. However, I think building a custom .net object would be the least work and hassle. In other words, you flip your approach around. You launch Access first and it uses your .net application as a COM object. That way, any routine in your application would be useable and callable from Access VBA.
The ONLY reason why I would walk down the road of implementing a call back if the main .net app REALLY needs to receive messages from Access as opposed to Access simply calling + using a custom .net object to display the process information. (so VERY easy to build a .net object for showing progress).
So best bet is to adopt the idea of turning your existing application into a COM object to be consumed by Access. This is VERY easy.
Once you do above, then
Access is launched first and then creates the instance of your application. (and thus can call/use any .net sub or function expose in your .net application from VBA).
And even if you don’t launch Access first, Access on startup could grab a running instance of your application (just like you can pick up a running instance of Excel or Word in Access/VBA).
Last but not least;
You probably can implement call backs, but I simply don’t know if this is possible without having to create a .net COM object as a go-between. As such, likely best to have Access just pick up a running instance of your .net application anyway.
So I would expose your .net application as a COM instable object.
So least amount of work would be to create a separate .net COM object that access uses, but if you assume for future that VBA should be able to call any vb.net routine then I would thus just build your .net application as a consumable COM object.

Related

Synchronize to Form Thread - without usinig Form

using Windows Forms it is possible to synchronize incoming data (like from events) which runs on another thread as that one which has created the form. However it is not allowed to update the form directly, therefore InvokeRequired and Invoke are used. Works great for me.
Now I want to move the logic part of my application (contains serial, networking, ... hence the threads) into a library. Also I want to have the synchronization in there, so there is no need to take care about that externally.
For now I create and pass a dummy form to my library, which is ugly, but works. At least some kind of. .Net Core has no Windows Forms. (3.0 has, but Windows only).
Even after reflecting the Form class I was not able to reproduce the synchronization process, so I could add it to my classes.
Does anyone know how to archive the same behaviour without Windows.Forms?
I use .Net Framework 2.0 and VB.Net, but C# examples are welcome as well

Can I create a custom MS-Access API to interface with other applications?

I was searching MSDN and several other Access forums for creating custom Access API's, but didn't see anything related. Is this possible? Can custom ODBC's be created for Access?
Hum, this is confusing since it not clear if you are talking about building interfaces with Access to OTHER systems, or are you talking about a building an Access interface that other programs can use?
ODBC is a "driver" provided by vendors to allow use of their SQL database systems and is something that you don't create in Access or FoxPro or in VB for that matter. In fact I can't think of any data centric tool that would allow you to build your own ODBC driver. So this suggests some confusing here.
Extending Access.
Access for the last 20 years has had the ability to consume ActiveX (COM objects). So how one extends the ability of Access to communicate with other software and systems REMAINS the COM object choice. So for example one can use the MSXML library from Access (a COM object) and this allows one to consume web pages, and specify XML which opens the door to consuming web services. In fact I used MSXML to consume SharePoint web services for example.
However for consuming web services it is usually far better to build COM object with tools that can "already" consume web services. So one would use vb.net to create a COM object and THEN reference that COM object from MS Access (so just like we set a reference to outlook or word from Access, such COM objects are not limited to applications you purchase, but you are most free to create your own COM objects VB6, vb.net, c# or whatever).
Access does NOT have the ability to create ActiveX (com objects), but it certainly can consume ones you build in other languages.
NOTE: I am using the term ActiveX and "COM object" interchangeability here – they are the same basic technology.
So if you ever used a desktop computer and any windows development tool, you find that "COM" objects are the choice and REMAIN the choice of how to extend Access, or even that of VB6. With managed code, things change somewhat, but vb.net is quite happy to create COM objects that work well with Access.
While Access cannot as a tool create ActiveX (com) objects, you can certainly compile Access code into an accDE and THEN reference that library code from the VBA tools->references. So you can extend and share VBA code in one library with "many" applications, but this is not a com object.
So just like in the past to extend desktop programs, you can build such extensions as COM objects and consume them from Access – this is the standard approach to extend Access to say consume web services.
Access cannot be used to build individual COM objects as UI parts, but the STANDARD means in our industry to create COM objects is able to be consumed by Access.
Last but not least:
Any application you build in Access CAN BE CONSUMED as a COM object by other applications. So while you cannot build individual COM objects, your WHOLE application is in fact a com object.
So if you build an Application in Access then using Excel, VB6, FoxPro, another Access application, C++, or C# or vb.net? They ALL CAN consume your application as a com object by simply creating an instance of Access and your application.
So any development tool that supports "com" can consume your application as a com object. So any standard development tool, article or system for the last 20 years can consume your Access application as a COM object.
So while Access cannot create individual COM objects, the resulting Access applications are in fact a legal com objects that ANY application that supports COM can create an instance of your application. This means your code and VBA subruties etc. are callable from the host application that created the instance of your application. In theory any VBA code exposed as "public code" are thus able to be called from that other program.
So the "interfaces" available to Access remain the SAME as the near 20 year history of MOST desktop programs running on windows – that interface is COM.
So you have 20 years of history and 20 years of examples on the internet to have fun with - any example that shows how to create and consume a com object should work for your access application.

Adding a COM interface to an existing application (EXE)

I intend to add a COM interface to an existing application (which, by the way, is written in C++ using Win32). I have some experience using COM objects, so I know the basic COM concepts of interfaces, etc., but this is the first time I'm actually implementing a component.
Ultimately I want to be able to use the COM interface to automate my application from scripts such as VB. I understand that there are two steps:
My application must act as an out-of-process server (i.e. I have to use MIDL and generate code for a proxy DLL and a stub DLL).
Once I have the server I can add automation capabilities by implementing the IDispatch interface.
Since the server-in-an-EXE thing with MIDL and what not is already a bit steep, I wanted to get a grasp on all that first before moving on to IDispatch.
I am reading the book "Inside COM" by Dale Rogerson and have completed the chapter on servers in EXEs (the following chapter will cover Automation).
The "Servers in EXEs" chapter provides example code that implements a server and a client. But it is necessary to start the server manually. This confuses me. Obviously, when my application (= server) is used by a client process, this extra manual step should not be necessary. Is there no mechanism to start the server automatically? Or is automation necessary to achieve that? At the moment, the prospect of having to start my server manually (once I even have one) makes me doubt I am moving in the right direction.
Hopefully someone with more knowledge of this can see what information I'm missing and point me in the right direction.
No, COM servers are not normally started by hand. Not sure why the book proposed it, possibly because it wanted to avoid talking about the registry keys you need to allow COM to automatically start the EXE. It isn't otherwise very complicated, you register the Application coclass of your app with the LocalServer32 key value giving the path to the EXE.
It is however not completely uncommon, especially with an existing program. One design decision to make is whether you let the client code completely control your program. Or if your program already has an existing user interface but you also want to expose services to other code. In the latter case it makes sense to let the user start the app by hand, like she'd normally does.
When your application is registered as LocalServer32, it will be invoked with the commandline specified there if no running process has registered a factory object for your CLSID yet.
This way, you can get the best of both worlds -- if the application is running already, this instance can provide the server side, and if it isn't, it will be started.
Automation is completely orthogonal to that -- your component becomes Automation compatible by implementing IDispatch.

.NET out-of-process COM objects sharing static instances in API calls

It's hard to explain our situaction.
We have a 3-tier application. The engine is a DLL coded in C++, then we have a VB6 ActiveX EXE that access to the engine via API calls, and at the top level we have a Excel Addin (in C# using VSTO framework) calling the middle layer with an interop DLL. At this time, each "connection" from the Addin to the engine creates a new EXE (VB6 uses API calls to access to the engine) and all works fine.
Now we are moving the middle layer to .NET, it works 'presumably' fine (it pass all our Unit test) but, we found an error when we open 2 "connections" at same time (ups, no unit test check this situation because it's a new behavour). The DLL have static objects that it's shared over all instances in the same process and we have interactions between "connections". In our old version each "connection" creates a new EXE with no memory sharing between processes, now it's the same process and they share memory and the static objects.
Following the tips from this question. We tried to build a COM EXE in C# to do an out-of-process objects in the middle layer but we have the same result. They share the static objects, at the end, each connection not creates a independent process.
It's clear, but not affordable at this time, moving API calls to ATL or changing the static objects to instanciable references with a handle and change all the API calls to get/set this handlers. I reviewed all examples in MS All-in-one but I didn't find any solution. Neither it's possible to keep only one connection at time, each workbook can have one connection and in the future we want to explore a Web application with multiple connections at same time.
Any suggestion?
Thanks in advance,
Whether COM starts new EXE per each COM object, or uses single EXE to instantiate all the object is controlled by flags parameters passed to CoRegisterClassObject. See
http://msdn.microsoft.com/en-us/library/ms693407(v=vs.85).aspx, and
http://msdn.microsoft.com/en-us/library/ms679697(v=vs.85).aspx
You need to pass REGCLS_SINGLEUSE or REGCLS_MULTI_SEPARATE flags.
Now, the trick is to pass this flag, as you might not call this method directly - the details depend on how you implemented the COM EXE.
it's not clear from the question, but it sounds like the "middle layer" you have was built as a VB6 EXE, and you're trying to replace it with a .net DLL. If that's the case, you'll definitely get the behavior you describe.
With a VB6 EXE com project, instantiating a new object starts a new process. With a .net dll (or a Vb6 dll really) you +won't+ get a new process.
You'd either need to create a .net EXE that exposes COM objects just like your VB6 exe does, or (sounds like you've already investigated this) you'll need to refactor your EXE objects to properly handle multiple instances within a single process.
Honestly, it'd probably be better to do that latter, since relying on singletons like this is generally a bad code smell. But it a pinch, you should be able to replicate the behavior of the VB6 exe with a .net project. You just can't do it in a dll.
Was your middle layer created in .Net? If it was, you might be facing the issue that your COM class is been created as a native .net object instead of a COM object. The solution usually involve using Primary Interop Assemblies. Take a look on this SO question to see if it matches your problem.

VB: get compiled DLL's calling application info; COM security

Through COM, one can potentially gain absolute control over a target system. For example: using javascript's ActiveXObject object in IE, one can create certain objects which were designed to have direct access or interaction with system properties and files. One would think common sense dictates users disable ActiveX features in IE immediately after installing the browser to ensure their system is protected while surfing the net, or at least paying close attention to which websites they permit. But, I doubt many average PC users know how or why to do this, or just get tired of mirco-managing it over time. I think any PC user or admin my COM class caters to would greatly appreciate not having to deal with that. Thankfully it looks like IE versions come packaged with ActiveX disabled by default nowadays.
I've built a very versatile COM class library in VB. I didn't intend for it to be callable from any website, but that feature is just part of the COM platform. I'd like to prevent the library from being called from IE unless the website is on a white-listed domain to proactively protect the user (and ultimately their entire intranet) from harm from malicious websites. What would be the best method in VB.Net to tell which application called my DLL, to be able to tell if it was called from any command or process originating from IE? And, what domain called my dll?
Edit: I believe this might be a duplicate. See: Calling Assembly to get Application Name VB.NET
System.Environment.GetCommandLineArgs()(0) gets me the calling application path. With this info, I can compare it to a black/white-list of applications. Problem solved for now.
Don't mark the control as Safe for Scripting.
Default security settings will not allow such controls to be scripted.
Self-answer, and possibly duplicate, I suppose. See System.Environment.GetCommandLineArgs()(0) from Calling Assembly to get Application Name VB.NET
In this case, the class never was marked as safe for scripting and the intent was already never to mark it safe. The issue was how to obtain the calling application info so I could add additional security measures in case those which the calling application had were not enough.