How to interact with another programs with OLE Automation in C#? - c#

I create an application with C# that can read item data from SQL Server and push it to the scale system named "SLP-V Ishida Retail Scales". They have an interface "SLP-V Automation Interface" that allows user programs to interact with their systems. This is the note from help page in SLP-V :
The automation interface (also known as the "COM (common object model) interface") provides a method for user programs to access SLP-V functions. The most common application for this is the use of VB Script to automate SLP-V operations such as importing host files. However, the automation interface can be used from any programming environment that supports automation (COM), and this is the preferred method for incorporating SLP-V functions into end-user applications.
This topic provides a reference for the methods and properties of the SLP-V automation object and includes some sample programs.
SLP-V Automation Object
The SLP-V automation object name is "Ishida.Slp.Scripting.CommonApi" and the type library file is "SlpScripting.tlb".
My question is, does the C# language allow us to interact other programs using OLE Automation? And if the answer is yes, how do I interact with my program?
I mean like calling their method. Because I can't add SlpScripting.tlb as a reference. It says
A reference to 'SLP Scripting Interface ' could not be added
The ActiveX type library 'SlpScripting.tlb' was exported from a .NET assembly and cannot be added as reference. Add a reference to the .NET assembly instead
And I have searched Google about this, but I didn't find the answer.
Finally found a solution
I don't have to add reference in c#, instead of just using :
System.Type objType = System.Type.GetTypeFromProgID("The name of progID that you want to call");
dynamic comObject = System.Activator.CreateInstance(objType);
Example ProgID = "Ishida.Slp.Scripting.CommonApi".
And then just call the function / method that exist in that object, for example :
comObject.LoginToHost("localhost", 8085, username, pass);

OLE automation is old wording for what we now call COM. And yes, .NET can access COM very easily (starting with .NET 1.0).
You have these options:
Method 1
First "register" the COM library on your development system. Look in the documentation of the SLP system, probably this was done already during setup. If not, normally a COM DLL can be registered manually with regsvr32 XXX.DLL. Be aware of 32/64 Bit issues (if you want to register a 32 bit COM DLL in 64 bit Windows, use C:\Windows\SysWOW64\regsvr32.exe).
Then your COM DLL should be listed in Visual Studio if you go to
Add Reference ==> COM
as "SlpScripting Type Library 1.0" or similar.
Then, add a "using SLPxxxx" or similar (Intellisense should show the real name).
After this, you should be able to create a new instance of your COM object.
For additional help, search for "C# COM interop", you will find lots of informations.
Method 2
Open a Visual Studio command prompt, and enter:
tlbimp SlpScripting.tlb
A DLL will be created, which you can add as a reference.

Related

How do I call a COM object in C# that I registered using RegistrationServices

I'm experimenting with COM objects and created a simple COM service that acts as a calculator with add, subtract, multiply, divide (details not important).
I then wrote some code to register it dynamically with a C# application
Assembly asm = Assembly.LoadFile("C:\\...COMCalc.dll");
RegistrationServices regAsm = new RegistrationServices();
bool bResult = regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase);
After registering it I've been able to use the service from an IE browser in javascript.
var num1 = 2
var num2 = 2
var objTest = new ActiveXObject("COMCalc.COMCalc")
alert(num1 + " - " + num2 + " = " + objTest.Subtract(num1,num2))
I'd like to now be able to test it from my C# Application so I can have a register, unregister, and test method for my COM object. I've struggled to find the documentation for how to do this. Any Ideas?
Bonus: I also would like to access it with the GUID defined in the COM object as opposed to the COMCalc.
regAsm.RegisterAssembly(asm, AssemblyRegistrationFlags.SetCodeBase)
By writing your own custom registration method, you are missing out on the normal way that COM client programs or unit testers will exercise your code. They'll use the type library of your COM component, a machine-readable file that describes the types that you expose from your component. It is the COM equivalent of .NET metadata.
You get a type library by using the normal way to register, either by using your project's "Register for COM Interop" setting or by running Regasm.exe with the /tlb option. Or by running Tlbexp.exe to generate it manually.
This however does not let you test your component with a C# unit test, you'd normally use Project > Add Reference > Browse and pick the .tlb file. But the IDE refuses to accept it, it can see that the type library was created from a .NET assembly. It insists that you use a normal assembly reference instead, picking the DLL instead.
There's a very good reason for that. You can fool the IDE by using late binding but that does not fool the CLR. In other words, you are not actually testing the COM interop at all. You might as well use the normal way to add a .NET assembly reference. Truly testing the component requires using a COM client written in a non-.NET language. Not that many practical ones around anymore, you could use a scripting language like Javascript or VBScript. The closer it is to the actual language and runtime environment that is going to use your component, the better. If you are going to use it in a browser then something like Selenium or HtmlAgilityPack would be wise choice.
Nobody ever likes to hear advice like that. You fool the IDE by late binding, very similar to what you did in the browser:
Type type = Type.GetTypeFromProgID("COMCalc.COMCalc");
dynamic obj = Activator.CreateInstance(type);
dynamic result = obj.Subtract(2, 1);

Reusing a COM interface

Let me elaborate, before someone is thundering down on me with "Never reuse a COM interface!!!"
We implemented an COM interface on a program which is discontinued. We now actually buy a similar piece of software from a third party (so I can't change that!)
The COM interface is still used by many (third party) programs. Now I have to integrate the new piece of software with these programs (which I can't change because they are third party).
So I think I need a proxy: This proxy will reuse the COM interface so none of the third party programs will be able to tell they're not talking to the discontinued software. Inside the COM object, I'll 'translate' the commands and forward them to the new piece of software.
I'm using C# to build my proxy and I've used the tlbimp.exe to generate a DLL from the type library of the old program.
I'm referencing this generated DLL in my COM project where I provide a implementation for the given interface.
Next, I register the output DLL from my project and use a old client to call the proxy.
It returns the error: Unable to cast object of type 'Lib.ApiClass' to type 'Lib.ApiClass'.
Why is that?
Should I use a different approach?

COM export method from object in .exe application [duplicate]

I currently have a .NET class library written in C# that exposes its functionaility via COM to a C++ program (pre-.NET).
We now want to move the library out-of-process to free up address space in the main application (it is an image-processing application, and large images eat up address space). I remember from my VB6 days that one could create an "OLE automation server". The OS would automatically start and stop the server .exe as objects were created/destroyed. This looks like the perfect fit for us: as far as I can see nothing would change in the client except it would call CoCreateInstance with CLSCTX_LOCAL_SERVER instead of CLSCTX_INPROC_SERVER.
How would I create such an out-of-process server in C#? Either there is no information online about it, or my terminology is off/out of date!
You can actually do this in .NET (I've done it before as a proof-of-concept), but it's a bit of work to get everything working right (process lifetime, registration, etc).
Create a new Windows application. In the Main method, call RegistrationServices.RegisterTypeForComClients- this is a managed wrapper around CoRegisterClassObject that takes care of the class factory for you. Pass it the Type of the managed ComVisible class (the one you actually want to create- .NET supplies the class factory automatically) along with RegistrationClassContext.LocalServer and RegistrationConnectionType.SingleUse. Now you have a very basic exe that can be registered as a LocalServer32 for COM activation. You'll still have to work out the lifetime of the process (implement refcounts on the managed objects with constructors/finalizers- when you hit zero, call UnregisterTypeForComClients and exit)- you can't let Main exit until all your objects are dead.
The registration isn't too bad: create a ComRegisterFunction attributed method that adds a LocalServer32 key under HKLM\CLSID(yourclsidhere), whose default value is the path to your exe. Run regasm yourexe.exe /codebase /tlb, and you're good to go.
You could always expose your .NET class as COM classes using InteropServices and then configure the library as a COM+ application. The .NET library would run out-of-process and be hosted by a DLLHOST.EXE instance.
Here is an article in MSDN that covers all aspects of how to create COM localserver in c# (.net): link
Your post started a while ago and I had the same problem. The following link is absolute gold and tells you everything
http://www.andymcm.com/blog/2009/10/managed-dcom-server.html

Adding an in-proc COM Server object to a C# application

I am new to COM and need to add a Server COM object to my c# application so I can call its methods and implement events. The documentation I have says this requires a COM-aware language such as Visual C++. My app is written in C# so I'm not sure how this is going to work. Any direction would be appreciated.
I am writing an app that communicates with a serial hypercom terminal. The device came with a DLL (the com server interface) that I will need to figure out how to use in my c# application.
I added a reference to the DLL to my project, which shows up as a type library. In object explorer I get interfaces, and some classes etc.
Thanks,
Matt
You can add the COM object as a reference. .NET will create an interop assembly to work with the COM object, just like it was a .NET type.
CComObjectRoot can be used as a base class for all COM objects implemented with ATL. As such you don't have to worry to implement something similar in C#, the required methods (AddRef, Release, QueryInterface) will be generated by tlbexp for classes that are tagged with ClassInterface.
STDMETHODIMP is a define which serves to declare the most common methods that can be called from COM (#define STDMETHODIMP HRESULT STDMETHODCALLTYPE). Again if your class is tagged with ClassInterface you will not have to worry about.
Such construction is required in C++ when your class implements several interfaces. I think this is not required if you tell C# compiler that your C# object implement IDispatch, IFPESOlementationEvents. The appropriate code will be written automatically by the compiler.
Probably everything will not make much sense if you are new to COM and C#, I'll suggest to take a look at the various introduction that you may find on the web, like this.

Side-By-Side COM Interop with C# and VBA

I'm not talking about calling a VBA COM from C#... the other way around!
What I would like to do is call a C# library using VBA in MS Access without registering the DLL. I've been playing around with side-by-side interop for a while without success and it has finally occurred to me that a mdb.manifest is probably not an acceptable replacement for an exe.manifest (probably obvious, I know, but I was trying to be optimistic).
My question: Is it possible to get VBA to load a side-by-side COM component?
Or, is there another way to use an unregistered C# library in Access?
(Before you ask, my reasons are: there is absolutely no way I will be granted access to my client's Windows registry -- that's why it was written in Access in the first place. And, I will need to implement the same functionality in a C# application soon and rather not do it twice).
To add to the already existing answers: with .NET 4.0, it's actually quite simple to consume a C# dll in your VBA project without registering the COM.
EDIT: I just tried this with the mscorlib.tlb and mscoree.tlb that are in C:\windows\Microsoft.NET\Framework\v2.0.50727-- loading an assembly compiled in 3.5-- and it worked just fine. So apparently you don't need .NET 4.0.
The below is an example of how to use a C# dll in your VBA project. It is slightly modified from this answer.
1) Add references to the following type libs your VBA project (Tools->References):
C:\windows\Microsoft.NET\Framework\v4.0.30319\mscorlib.tlb
C:\windows\Microsoft.NET\Framework\v4.0.30319\mscoree.tlb
(use Framework64 folder if you are running 64-bit Office)
2) In your C# project, make sure you add the [ComVisible(true)] attribute to your class:
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace VB6FuncLib
{
[ComVisible(true)]
public class VB6FuncLib
{
public VB6FuncLib()
{ }
public void test()
{
MessageBox.Show("Test Successful");
}
}
}
You don't need to check the option "Register for COM Interop". That's only for building a standard COM object. You don't have to check "Make Assembly COM Visible" either, unless you want the whole assembly to be visible (that would also eliminate the need for the COMVisible attribute).
3) In your VBA code, add a new module with this code:
Sub Test()
Dim Host As mscoree.CorRuntimeHost
Set Host = New CorRuntimeHost
Host.Start
Dim Unk As IUnknown
Host.GetDefaultDomain Unk
Dim AppDomain As AppDomain
Set AppDomain = Unk
Dim ObjHandle As ObjectHandle
Set FS = CreateObject("Scripting.FileSystemObject")
Path = FS.GetParentFolderName(CurrentDb().Name)
Set ObjHandle = AppDomain.CreateInstanceFrom(Path & "\VB6 Function Library.dll", "VB6FuncLib.VB6FuncLib")
Dim ObjInstance As Object
Set ObjInstance = ObjHandle.Unwrap
ObjInstance.test
Host.Stop
End Sub
4) Copy the DLL into the same folder as your Office project and run the Test() sub in VBA.
Notes:
It should be noted that one of the limitations of this technique is that it won't work if the .DLL is stored on a remote network share. One simple solution would be to copy it into the same local folder on each PC where it is being used. Another solution would be to include the binaries in your Access app/VBA project, and have MS-Access export them. One way that could be accomplished would be by storing them in Base64 in a table or spreadsheet, then converting them and exporting them as binary.
I was able to get early binding (and therefore Microsoft IntelliSense) to work by creating a type library to go with the DLL (by using tlbexp), and adding a reference to the TLB in my VBA project, but it does complicate matters a bit because it requires your VBA app to know where both the DLL and the TLB files are (and also requires someone to make sure they are there).
You don't have to own the exe to use SxS, SxS is another word for Activation Context.
If you can import the relevant win32 calls into vba (and you can), then you can use the activation context api to load your manifest file.
More on the subject and some examples can be found here.
The problem is that to use SxS, you need to own the exe to set up the config to load the SxS assembly. You don't "own" Access, and while you could drop the right config in to cause it to load your .NET COM stuff sans registration, it wouldn't be a "good citizen" move.
If you get tricky with shimming, you can set up an unmanaged DLL (or a hacked C# class library with a dllexport, see this, for example) with an export that will load the .NET framework, create an instance of a COMVisible DispInterface managed type and return it (the method should return IDispatch). Then write a VBA declare to your DLL export function (declared as returning Object). If this doesn't make sense, you probably oughtn't try it... :) I've done this before in a similar situation, and it does work, but I don't have a sample to point you at.
C# libraries are not regular DLLs. They're more similar to COM libraries which need to be registered (just like ActiveX controls) before being used; especially when called from non-.NET code.
(Unless, of course, things have changed...)

Categories