Hi folks. I'm not sure if I'm heading in the right direction with this - I haven't really touched on ActiveX EXEs and automation servers before...
I have two applications which both access data through an object model - called DataStore - which is in a DLL. It exposes classes which simply wrap records in the underlying SQL tables. App1 is effectively a database maintenance utility, the user defines his world by going in here and having a user-friendly front-end. App2 does interesting simulation work using the same data, and therefore it uses the same DLL for its DataStore. Both Standard EXEs, both work fine.
I need to develop network capabilities for App2. The idea is that someone else comes along and runs their own simulation, using the same data as the first user. Due to the way this can be used, any machine can host data, but at any time, it's only one machine whose data is relevant. All subsequent users in a session use the same data set, so it has to stay in its own address space.
I've figured out a logging in protocol using Winsock. If I start App2, I'm going to say whether I'm a server or a client, then either (server) I either use my own data, or (client) the selected server sends me enough info down Winsock for me to use GetObject on the server's copy of DataStore.
1st question: is this valid? Can I access a class built in VB6 using GetObject? I've seen somewhere that it's not allowed, but maybe the use case for that was different. One way or another, I have to bind server and client on the fly, they're not aware of each other's existence until the app is launched.
Of course, if I'm a server, I can access my own data, so I use CreateObject to get my own reference to DataStore (and now my machine hosts the only instance of it during this session, amongst all users).
My scheme is to covert App2 into an ActiveX EXE, exposing an Application object the same way Excel does it, so I can remotely access DataStore (and one or two other application-specific properties and methods) through my Application object.
I tried setting it up this way, and the app no longer opens up its own copy of DataStore properly. It tells me 'ActiveX component can't create object' as soon as I try to access any of its properties.
What's going on? If ActiveX EXEs isn't the right way forward for me, then what's the alternative?
In Project menu, chose NameProject Properties menu (submenu of Project menu); in Project type of Genaral tab chose ActiveX Exe and in Startup Object chose None. And OK. Have fun!
Well, I tried that, and now the app doesn't do anything at all if I start it as Standalone. Remember, if I'm the first person to start the app, my instance of it will act as a server for anyone else coming later. But I must have a user interface, or I can't do anything with it myself!
By the way - I did say I haven't done much with ActiveX before, so while I'm by no means a newbie with VB (Been using various versions of it for over 15 years), I'm not above going through the step-by-step instructions which MS provide in their online documentation. In other words, the Coffee project, which is now located here... http://msdn.microsoft.com/en-us/libr...(v=vs.60).aspx
I found it impossible to follow the instructions. Specifically, when I get to the stage of creating a test project, the instructions tell me to set a reference to the .vbp file of the ActiveX project I just created. It specifically tells me not to set a reference to the .exe I just created from that project. Not possible - My IDE doesn't allow me to set a reference to the .vbp file. I can't get much further with MS' instructions for getting my head round this!
In fact, that problem should only prevent me from being able to debug both projects at the same time. I went ahead and set my reference to the .exe rather than the .vbp. It should at least behave the same way when I run the test project. But it doesn't. I don't get the form coming up which the ActiveX component should be displaying. I just get nothing happening, rather like the nothing which happened when I tried Andrew Peter's suggestion for converting my own project. Is this indicative of some 'problem' on my setup? I'm using VB6 with SP6 on Vista.
Before going down the ActiveX EXE road... the problem you are trying to solve seems vague.
There is no need for some DataStore DLL, you already have perfectly fine and very rich data access object models such as ADO or even DAO. If multiple instances of your App2 need access to the same data, why aren't they just opening the same database?
It almost sounds like you are trying to create some sort of peer to peer network affiliation among the instances of App2. Can we assume they are all on the same LAN? Do you have some reason to avoid using a common machine that hosts the database? Even a file server in common with a Jet MDB file on it can serve for this within limitations.
If so this seems a bit odd since you mention "underlying SQL tables" - though even that's weird terminology. Or when you say "SQL" are you trying to say "SQL Server" which is an entirely different thing?
Well, there IS a need for my DataStore DLL, since really, it is middleware. When I said SQL, yes - I should have said SQL Server. And yes, you're right about trying to create a peer-to-peer thing amongst instances of App2.
Here's the unabridged version of how this thing is supposed to work. (Apologies for simply copying and pasting from another forum site where I've been asking similar questions.)
----
I've been developing a project for a while, with the idea that eventually it would become a distributed application. There's one object model which everyone gets to use, but a candidate object model could reside on anyone's machine. It's actually for hobbyists - model railway people. Any one of them might store data in his own database to define his own model railway (railroad, if you're in USA), and when he's in his own den, this is the data he uses. If he takes his laptop along to a friend's system, it's his friend's system which supplies the data, through the object model on that machine.
I've developed a .dll for encapsulating the object model, alongside a Standard EXE for front-ending it. That's all fine, and the user can define his model layout and read it back during another session. I've been working on a second Standard exe which uses the same .dll for its data retrieval, but this time it's for giving the user a set of operating interfaces, which he can use for actually running the model layout he defined. It's this second app which I need to convert into an ActiveX EXE.
My idea is that the app uses a Sockets exchange to communicate with existing instances of itself, running somewhere else on a LAN. I've pretty much got that part sorted out, but basically when the app starts, it broadcasts a message asking for server instances to identify themselves. If a reply comes back, it's in the form of a packet containing sufficient info that the new instance can use GetObject to retrieve a reference to the server's data (including the computer name, so I can get the reference across the network). That server also has its own user interface, of course. I can also start the app in server mode, and it then uses its own local data, but in this case it uses CreateObject to get it. If another instance tries to connect with me, then its GetObject must be given a reference to the object model I just retrieved. Hope that makes sense!
This could have gone on the end of that lot, I suppose.
Yes, there's a good reason for avoiding using a common machine for the database. Every machine connected to the LAN will be hosting a usable database, but during any one operating session, it's only the machine owned by the person hosting a session whose database is relevant. Think about how the system would be used. When I sit in front of my own model railway, my own database is relevant. When I go over to my friend's place and connect to his system, it's his database which is relevant.
Well you don't want to use an ActiveX EXE then, that's clear enough. It would require DCOM configuration and the machines normally need to be in the same Domain or Workgroup (and that's normally a bad thing for foreign machines).
◦In order to get two machines working with DCOM across untrusted domains, the AuthenticationLevel must be set to NONE on both machines. However, if you do this, the impersonation of the client will fail. There is also a requirement that the user names and passwords must be identical in both domains. In this case, Authentication can be enabled.
It really sounds like you have only one choice if the "data" here changes (gets updated as the trains move, etc.). That would be to create your own application protocol, probably using a "dumb" transport like TCP/IP to avoid security issues inherent in normal Windows networking. Otherwise if you just had to share static "layout" data, at startup Player A could ship a file to Player B and then go from there.
So this is just grunt work. Lots of time to design it (especially to avoid planned obsolescence) and then to code it up.
If your "object model" consists primarily of persistable objects you might save some work shipping PropertyBag contents across the wire along with info describing what object it is and what should be done when the server gets it. But people mostly avoid that because of the interface versioning issues and either ship around ad-hoc structured data or XML, JSON, etc.
Well, that's possibly the worst news I've had in a long time. I've had some experience of coding asynchronous comms over Winsock, and it quickly gets too complex to understand. I had an idea that this long road would eventually lead me into a whole new world of pain, and perhaps this is it.
I've designed the object model for this with the idea that I would be able to use DCOM, and I'd still hope to go along that route if possible, simply to avoid a huge rewrite of everything I've already done. There are already over 400 different objects within it. That's complexity in anyone's book.
Let's give a 'for example'. You probably know that in the real world, trains are signalled between signal boxes using a protocol involving electro-mechanical instruments, one in each box at the two ends of the line. These are known as 'block instruments'. There are many different kinds of block instrument, too many for me to hard-code into the system, because I don't know what they all are. However, they each have a number of characteristics which the user can define when he sets up his layout (using the first of my apps, which works fine). So it may have a base image, a set of alternative images, one of which is overlaid onto it according to the current setting of a switch (which the user is able to change when he's viewing it), and another set of images to show him how the signalman at the other end has set his switch.
Instruments work in pairs, which is easily simulated within the object model. OK, well one 'player' might be viewing the instrument at one end of the line, and another player is viewing the instrument at the other end. These two players are operating different signal boxes. If I'm player A, and I have (within my copy of App2) a UserControl which is designed specifically for allowing me to view a block instrument, I'd just like to change the switch, and let player B see the change in a similar UserControl displaying the instrument at his end. In some circumstances, maybe I'm not allowed to make the change I'm trying to make. The protocol is enforced by the object model, and prevents me from doing so.
So I've coded the Block Instrument object with an event to say it has just been changed by the signalman at the other end. The event is seen by player B's UserControl, which then gets the current image (base image plus relevant overlaid images) and displays it. Using DCOM, that's a piece of cake. If I now have to change things and start sending images around using Winsock, well... Ouch.
If there's some way of getting a 'foreign' machine into the same domain or workgroup, for example by transmitting the necessary settings in the initial logging-in procedure which I already have, I'd work on that basis. I'd be surprised if there isn't a way of having a machine put itself into the necessary workgroup 'on-the-fly' from within a VB program.
I can appreciate that generally, this would be a bad idea for security reasons. However, my reasoning is that if I go to visit my friend's house to take part in an operating session, he must have already trusted me before letting me through the front door. It's not the same thing as running the app while hooked up to the Web and allowing someone in Moscow to come along with some code which recognizes what I'm running in order to get my bank account details.
I've thought a little about security. Let's take the scenario that the group of users are members of a club, and they take their club layout to an exhibition. There may be other layouts within the same exhibition hall, and let's suppose they are also running a copy of the system. It could be that more than one such layout has implemented its LAN wirelessly, therefore I might open a new copy of App2 and try connecting it to another system's 'server' instance. They'd be using the same port number for logging in, and even if I gave the system a means of selecting port numbers, that doesn't guarantee anything. So when a new instance is started, the server instance gets to see that, and must explicitly assign a user interface to it (from a selection of available interfaces provided by the object model) before the new instance is 'in'.
Sorry if it sounds like I'm rejecting your advice. I just think that if I change things and start using Winsock for a dumb application transport protocol, I'll be here forever. I was hoping to hear about how to use DCOM and ActiveX, rather than why I can't.
Don't worry about rejecting advice if it means a big rewrite. I'll be curious to see what others suggest though.
Joining a Domain or Workgroup requires admin rights and is completed by rebooting, and it can have drastic consequences for lots of things on the local machine. No, you don't do this from an application or casually at all really. The issues aren't so much about you "being secure" as not breaking the security mechanisms in Windows, losing user accounts, various things like service configuration, etc.
As the quote I posted above suggests there is a way to force cross-domain DCOM to work using local machine accounts, but it requires that the same users and same passwords are on both machines. In a Workgroup situation you pretty much have no choice since there are no Domain controllers to establish other trust relationships anyway.
This bites all but trivial .Net coders all the time because Visual Studio uses DCOM for Remote Debugging:
I really hesitate to encourage you on this path for a couple of reasons.
One, if you can't unravel the very simple Coffee Monitor sample you have a long way to go yet learning to write a successful DCOM server. There are many things that make sense for local COM that need to be done the opposite way for good results with DCOM. So you have a large investment in time to make yet even ignoring most DCOM configuration issues over a LAN (and here "LAN" means computers in the same Domain or Workgroup), i.e. just getting this working on one machine.
Then you have all of the LAN (same network) issues.
And then you get to all of the finicky cross-network issues.
At any stage you may run into an immovable object. This will likely lead to great disappointment.
So if you really want to follow this path I think you need to get the Coffee Monitor tutorial worked though and functioning, first on one computer, then between two in the same network (remember, network here is a software concept not hardware), then between two machines in different networks (probably Workgroups in your case). It is probably the simplest example you are going to find, since simple "OLE automation" à la Office applications doesn't really count.
At least you can get the mechanics worked out before you tackle anything as large as the application you are trying to ultimately create... and maybe get too far down a road that dead-ends on you.
A lot of your reply seems to be couched in terms of 'You have a long way to go before you're ready for an application of this magnitude, Sonny'. Well, I did say I haven't done a lot with it in the past, however I'm fairly sure I understand how the Coffee Monitor application should work. With my background, I damn well *should* understand it. It's not that I don't understand it, simply that I can't get it running using the simple step-by-step instructions. The reason I tried it at all was to make sure I could get an ActiveX application running on my machine. The very last thing I expected in that exercise was to discover that Microsoft's instructions do not match the behaviour of VB, i.e. that I can't set a reference to a .vbp file as they instruct me to do. That doesn't fill me with confidence about approaching any of the 'official' documentation I might find!
Well let's see what I can offer in the way of some direction.
First I am attaching an example with a server and client that works fine. Without too much trouble you can compile this code and test within one machine, just go through the ReadMe file for details. This is sort of a really stripped version of the CoffeeMonitor.
Then we get into DCOM deployment and configuration.
Since you're on top of things I'll assume you have read this MS KB article, but just in case here's a link:
This consists of an even simpler client and server, and a run-through on packaging, deployment. and configuration.
Now that still leaves a lot of details floating in the air since it assumes you are in a Domain network. But I did dredge up some vendor instructions on how they suggest their users get their own software to work on a Workgroup network. They state this for Vista but everything from NT 4.0 on up is roughly the same:
One tool you will need is the MMC snap-in for editing Local Group Policy, but note that:
Local Group Policy Editor is only available in Professional (Business), Enterprise and Ultimate Editions of Windows XP, Windows Vista, and Windows 7. Users of Windows 7 or Windows Vista Starter, Home Basic and Home Premium or XP Home will not have Local Group Policy Editor.
Well, I've spent the majority of the day researching, but without too much success. I understand perfectly how the two applications you attached work with each other without DCOM - but trying to discover what's actually going on within DCOM is less easy to discover. OK, it's all to do with marshalling and RPC, which I already knew. But I haven't yet found something which tells me exactly how DCOM and Windows between them determine whether a client gets to access a server.
The bottom line has to be - that the client end needs to know [some subset of information about the server's environment] and the server end needs to know [some subset of information about the client] before DCOM can facilitate communication between the two. What are those two subsets of information? If I can obtain the necessary information at both ends of the link I'm trying to make - with calls to read Registry entries, or with Windows API calls - then can exchange this using the Winsock link I've already established, and then each end may do whatever it needs to do before the magic GetObject call in the client instance of the application. But is that too late? Does DCOM have to be correctly configured even before the client application is launched? If so, I guess I have to go with my dumb transport protocol using Winsock, or use something like JSON.
Yeah, part of the problem is that the Anybody But Microsoft crowd (Mac, Open Sores, you name the flag and they'd salute it) did their usual best to undermine DCOM, which was a fantastic technology and even had an open spec. But they kept digging, finding, and publishing exploits, so Microsoft had to keep tightening the security noose on it tighter and tighter.
The underlying mechanism is Windows RPC, which suffered much the same fate and thus has the same web of security fencing.
A DCOM server has basically the same registry entries as a regular ActiveX EXE. If you go into DCOM Config and loosen all of the security specs for DCOM system-wide nothing more is required, except that some user must fire up the EXE manually under their own credentials. Then client access is limited only by fairly simple rules - as long as everyone is a user in the same Domain and security on the ActiveX EXE is open for any logged-on user.
The DCOM client however requires proxy registration on his machine too. That includes the Class IDs at the server, what machine the server is running on, etc. Even the target server address can be left empty though if things roughly akin to CreateObject() passing the server address are used at the client.
With slightly more configuration predefined remote users or groups can be registered with activation rights (if they are running on an NT-based OS, not Win9x). Then nobody has to manually start the server.
You can skim through Understanding DCOM, William Rubin and Marshall Brain, 1999 Prentice-Hall ISBN 0-13-095966-9 which may be out of print (and note I did not supply a link to any online PDF copy of the book that probably doesn't exist). However it is heavily oriented to the C++ programmer, and will lose most people by chapter 2 if not page 2.
Anyway, at a minimum the client machine must know the server's type information (COM proxy registration), server machine address, and acceptable user credentials. And normally these are fairly hard-coded during application installation or defined/updated later through administration tools.
In VB6 client has proxy stubs for all of the possible calls compiled into it, along with object marshalling logic so that simple data and objects can be passed ByVal and ByRef and so remote object methods, properties, events, callbacks, etc. work.
The compiler does a great job of this, but sadly the security plumbing is the deal breaker.
The next closest thing like it was the series of SOAP Toolkits for VB6, and they put an enormous burden of complexity onto the programmer. The .Net Team's mission was to kill VB, so we never got an alternative to DCOM with its ease of programming and without the security baggage. As the story goes the Team had many more Visual FoxPro people than it did former VB people on it. And of course it is no accident how much of the syntax and semantics of C# and VB.Net resembles stuff in Delphi.
Of course .Net was supposed to replace all of this. First we heard Web Services, but the performance is poor, so they added .Net Remoting. They had wanted to name this COM++ if you really want to gag. That effort really stank in many ways, so they came up with a bag of things (including DCOM and Named Pipes under the covers) and put a bow on it and called the mess Windows Communication Foundation (WCF)... even though it is as foreign to Windows as all of .Net is, like using an outboard motor for automobile propulsion. And being a nightmare even dedicated .Net shops struggle with the options.
With enough effort you will uncover sample code in C (buried in the Windows SDK and basically untouched since Win2K) that will make it somewhat easier to to dynamic DCOM configuration from your program. There is even a COM DLL floating around used by scripters to do DCOM configuration, but it isn't free and has distribution restrictions. But I am unaware of anything generally available and friendly to a VB6 programmer for doing this.
I will dig through old projects to see if I can find anything more dynamic and easy in VB6, but I don't hold out much hope. It has been 10 years since I was doing this regularly.
Before I lose the link, DCOM Architecture might be useful info, and more accessible than Understanding DCOM. Almost couldn't find that one, Microsoft seems to have removed the download. It is old but useful yet.
4am on Sunday morning, and I'm still here. This is not unusual. :-)
That stuff you put in your second post - about the two machines being in the same domain. I see that I could set a reference in my projects to something called Active DS. I've had a look in there, and it seems to be the Active Directory type library. Those domains you mentioned are probably the same domains used in Active Directory, right? I'm wondering if the solution is to use this (if the application is in 'server' mode) to add a new client into its own domain as part of the logging-in process as soon as it appears, and send back a user name and password which it has just created. The client gets this, along with the server name, and it now has everything it needs for getting in.
I haven't gone right through the document you linked for me in your last post, but it does *seem* that DCOM is going to dig the necessary information out of the client's registry at the time it sees the call to GetObject. So perhaps there's hope yet - when the application is in 'client' mode, it can put the required information into place in the registry after retrieving it through Winsock, but before establishing the DCOM connection with GetObject.
You can't have a Domain (or Active Directory) without having at least one Windows Server box running as a Primary Domain Controller.
Normal installation of a DCOM client (normal for VB6, for example via a PDW setup package) installs either a hard-coded "server address" put in during the packaging, or else prompts you and insists on one during install. And of course it also puts the usual component registration in as part of installation.
However...
That does nothing to make sure DCOM is enabled on the client machine, and nothing about the necessary firewall rules to allow DCOM traffic. And of course there are still many security settings that will need to be tweaked. Even the client will need ANONYMOUS LOGON configured for Allow Remote Access under Access Permissions. Server security configuration is more complex.
Your server machine must allow TCP 135 in, which the client uses via the RPC Endpoint Mapper. The client receives back (among other things) the UDP port that the client must have open to get things back from the server. By default this is in the range 1024-5000, and you should consider it randomly chosen every connection.
Normally the server needs UDP 135 open too, but you may get by without that.
A lot of vendors punt and just tell users "turn off firewalls on the servers and the clients." Shaky advice on a managed LAN, maybe nuts in the environments you are targeting.
You need this to successfully package your client using the PDW. I won't even try to get into the complexity with your DCOM client-and-server-in one approach at this point.