I already have a native code COM object, and am trying to instantiate it from C#. The Com is registered in DCOM components and in the registry.
This is what I'v try to do:
public static void Main(string[] args)
{
Type t = Type.GetTypeFromProgID("CaseConsole.Application");
dynamic app = Activator.CreateInstance(t);
dynamic c = app.Case;
c.OpenCase("17-16", 0);
}
If I try to instantiate the com-type then, I get the:
NotImplementedException
The execption is thrown at line:
dynamic c = app.Case;
As I set a breakpoint at this line, I'v looked into "app" and the error was already present
I'v looked in the Type t and it shows: IsCOMObject = true
As VBS it works great:
Dim App
Dim c
Set App = CreateObject ("CaseConsole.Application")
Set c = App.Case
c.OpenCase "17-16", 0
As VB.net it works
Sub Main()
Dim App
Dim c
App = CreateObject("CaseConsole.Application")
c = App.Case
c.OpenCase("17-16", 0)
End Sub
but not in C#
For the C# example I looked at the sources from
http://www.codeproject.com/Articles/990/Understanding-Classic-COM-Interoperability-With-NE
https://msdn.microsoft.com/en-us/library/aa645736%28v=vs.71%29.aspx
Equivalent code of CreateObject in C#
My guess is that i must invoke the methods with InvokeMember or a security thing...
Please can you help to get the c# example working?
Update rename case to c but that wasn't the fault.
Your C# program is subtly different from the VBS and VB.NET programs. The apartment state of the thread that runs this code is different. The exception comes from the proxy, it should be interpreted as "running this code from a worker thread is not supported". Nicely done. Fix:
[STAThread]
public static void Main(string[] args)
{
// etc..
}
Note the added [STAThread] attribute. It is technically not correct to do this, a console mode app does not provide the correct kind of runtime environment for single-threaded COM objects but if the VB.NET app works then you have little to fear. If you ever notice deadlock then you'll know what to look for. It worked when you used late-binding through InvokeMember because that uses IDispatch and it has a different proxy.
Great thank you Sami Kuhmonen, the solution was:
object c = t.InvokeMember("Case", BindingFlags.GetProperty, null, app, null);
t.InvokeMember("OpenCase", BindingFlags.InvokeMethod, null, c, new object[] { "17-16", 0 });
so I didn't realized the fact, that "Case" ís a property.
Solved
Related
I am working on a application which needs to communicate via COM interface with multiple CAD applications (not in the same time). I want to have nice and reusable code, but I came across problems with type casting of COM objects when I made generic application handle getter method.
What I tried so far:
This is the attempt I would like the most if it worked.
public static TCadAppType CadApp<TCadAppType>()
{
dynamic cadApp = default(TCadAppType);
//Here under Dynamic View/Message there is already an error
// Message = "Element not found. (Exception from HRESULT: 0x8002802B (TYPE_E_ELEMENTNOTFOUND))"
// cadVersion.Value evaluates to "SldWorks.Application"
cadApp = (TCadAppType)Marshal.GetActiveObject(cadVersion.Value);
//Following 2 lines of code are for testing purposes only, i am testing with Solidworks API
AssemblyDoc Assembly;
//The exception is thrown when I try to access some method from the Solidworks API
Assembly = (AssemblyDoc)cadApp.OpenDoc6("some parametras...");
}
Attempt using Convert class
// Another attempt using Convert class
public static TCadAppType CadApp<TCadAppType>()
{
dynamic cadApp = default(TCadAppType);
// cadVersion.Value evaluates to "SldWorks.Application"
cadApp = Marshal.GetActiveObject(cadVersion.Value);
cadApp = Convert.ChangeType(cadApp, typeof(SldWorks.SldWorks));
// Exception is thrown with the following message:
// Message = "Object must implement IConvertible."
}
I really thought that I am on the right track, since there is an article on Microsoft Docs website explaining how dynamic can help you with com interopt: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic#com-interop
Any ideas how I can do this runtime casting a keep my code as reusable as possible?
My software setup:
Win 10
Project is targeted for .NET 4.7.2
First Tests are with Solidworks 2019
Turns out that the my coding attempt 1 was valid c# code indeed.
I tried it using with Autodesk Inventor, and it works.
So the only thing left for me is to conclude that this is some bug from Solidworks and their COM interfacing.
Thank you Optional Option for your interest in the topic.
I'm usually a javascript developer, but for my company I just started learning c# in order to use the CimatronE 13 API to develop custom command line PDM tools for this 3D modelling software.
As I'm making progress understanding the programming language, there's this frustrating situation where I want to use an API endpoint method but I can't manage to get it working.
The Cimatron documentation says the following:
IPdm::GetRelatedDocuments
Syntax: RelatedDocuments = GetRelatedDocuments ( DocumentPath );
This method allows you to get related files from compound types of files, for example Assembly or Drawing.
Input: (String) DocumentPath,
Path to file. For example \Documents\Location\Folder\Document. The file must be Assembly or Drawing.
Return: (Variant) RelatedDocuments,
Variant type array each element of which contain two dimensioned string type array of files related to selected one.
This looks pretty straight forward to me, so I tried calling it in multiple ways from within the static void Main() method, but I keep getting errors:
var RelatedDocuments = interop.CimBaseAPI.IPdm.GetRelatedDocuments("path");
CS0120: An object reference is required for the non-static field, method, or property 'IPdm.GetRelatedDocuments(string)'
interop.CimBaseAPI.IPdm pdm = new interop.CimBaseAPI.IPdm();
var RelatedDocuments = pdm.GetRelatedDocuments("path");
CS0144: Cannot create an instance of the abstract class or interface 'IPdm'
Any ideas? It's probably simple but I'm still a noob with c# :p
EDIT:
Cimatron documentation about the interface interop.CimBaseAPI.IPdm:
Properties:
Get
Query (String, DocumentEnumType, DocumentEnumUnit )
Variant
Methods:
A lot, including Variant GetRelatedDocuments ( String )
As how I see it now... interop.CimatronE.IPdm is an interface and in order to use it's methods, we first need access to the Cimatron application. Using the application object, we can use it's methods to get the desired interfaces such as IPdm and use their methods.
The following code gives no errors from the compiler but does when executing. This seems to be related to version 13 of CimatronE, since the application object works just fine using version 12. A lot has changed between these versions which I think is the reason the API is not functioning properly, outdated.
interop.CimAppAccess.AppAccess AppAcc = new interop.CimAppAccess.AppAccess();
interop.CimatronE.IApplication CimApp = /*(interop.CimatronE.IApplication)*/AppAcc.GetApplication();
interop.CimatronE.IPdm pdm = CimApp.GetPdm();
var RelatedDocuments = pdm.GetRelatedDocuments("path");
Console.WriteLine(RelatedDocuments);
Please correct me if I'm wrong! (since I just started and still learning c#)
I ran into this same issue with Cimatron 14.
I needed to make some changes in Visual Studio for things run properly with Cimatron.
Run Visual Studio in administrator mode
Set your Debug & Release Solution Platform to 'x64'
It was also recommended to point the build path for release & debug to the same folder as the Cimatron references. In my case 'C:\Program Files\3D Systems\Cimatron\14.0\Program'. However my code appears to run fine without this.
I created the Cimatron Application with this code (VB.Net):
Dim gAppAccess As New CIMAPPACCESSLib.AppAccess 'Define an AppAccess object to get running active application
Dim gApp As CIMAPPACCESSLib.Application 'Define an Application object
gApp = gAppAccess.GetApplication 'Getting running active application
If gApp Is Nothing Then
gApp = New CIMAPPACCESSLib.Application 'Creating a new instance of a Cimatron application
End If
References: Interop.CIMAPPACCESSLib.dll & interop.CimServicesAPI.dll
It is my understanding that Cimatron 15 may also requires some manifest changes.
There is some help information in the Cimatron program under Cimatrom Modules > Cimaton SDK that may be mildly helpful.
I'm importing functions into my code at runtime from a .net dll I compiled on windows 7 64bit.
Pseudo:
delegate bool Test(int i);
var assembly = Assembly.LoadFrom(...);
var type = assembly.GetType(...);
var method = type.GetMethod(...);
Test test = (Test) Delegate.CreateDelegate(typeof(Test), method);
Now, when I call the imported function on windows 7 (which I compiled everything on) it works like it is supposed to, however, when I call it on windows 8 (64bit) it always returns true, even if the only thing the function does is "return false". In both cases, the function seems to be imported correctly. I checked with
Console.WriteLine(test.Method.ToString());
And it does output the function's name and type.
Any ideas what might be causing it?
Edit:
Ok here's the code. Still shortened and simplified, but without anything important cut out.
The dll code:
namespace InputController_Scripts
{
class script
{
public static bool Test_Paused(int color)
{
return false;
}
}
}
The program's code:
delegate bool DTest(int color);
Assembly assembly = Assembly.LoadFrom($#"{extraPath}{Config.GameName.ToLower()}.dll");
Type type = assembly.GetType("InputController_Scripts.script");
MethodInfo mInfo = type.GetMethod("Test_Paused");
DTest test = (DTest)Delegate.CreateDelegate(typeof(DTest), mInfo);
Console.WriteLine(test(123)); //false on win7, true on win8
Edit:
Ok, I found the problem. It wasn't to do with reflection (sorry, guys, yes, I was wrong about "not cutting out anything important"). Long story short, windows 8 had different regional setting and "0,10" in a config file was interpreted like "10" which was messing everything up. I changed it to "0.10" and now it works.
I'm exposing a C# class to COM using these attributes:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
[GuidAttribute("2325EBEB-DB5F-4D29-B220-64845379D9C5")]
[ComSourceInterfaces(typeof(WrapperEvents))]
in this class I have a function:
public void shutdownService()
This function is meant to be called just once from a VB6 client via COM Interop. Everything works fine. But somehow, it's being called more than once. My C# codes doesn't call this function directly. So I'm guessing the problem is in VB6 code. Unfortunately, that's not what the VB6 team thinks.
Is there a way to determine the caller of this function, ie. from my C#code or the VB6 code?
Right now I'm using a simple function to get the stacktrace:
public void LogStack()
{
var trace = new System.Diagnostics.StackTrace();
foreach (var frame in trace.GetFrames())
{
var method = frame.GetMethod();
if (method.Name.Equals("LogStack")) continue;
logger.Debug(string.Format("LogStack: {0}::{1}",
method.ReflectedType != null ? method.ReflectedType.Name : string.Empty, method.Name));
}
}
Obviously, I got somthing like this on the log:
2011-12-23 08:28:40,067 1 DEBUG (null) LogStack: Service::shutdownService
Since the only line of LogStack is the COM exposed function, I assume it's being called from vb6. But that's not enough proof for the VB6 team. Any idea how to really prove where function ?
You can try several things:
set a breakpoint in your code to trigger the debugger, then look at the call stack.
You could do an application dump here from visual studio and send it to them or screenshot the stack.
ex. Debugger.Break
http://www.netsplore.com/PublicPortal/blog.aspx?EntryID=12
Dump with "Savre Dump As"
http://msdn.microsoft.com/en-us/library/d5zhxt22.aspx
Use the com tracing
from a system level see
http://support.microsoft.com/kb/926098
I also recall a tool being installed with visual studio 6 do to this as well
I'm having a problem with the InternetExplorer in SHDocVw.dll. I also have mshtml.tlb referenced (while googling, I did read 1 comment that said I should have mshtml.dll referenced, but that this couldn't be done in Microsoft Visual Studio Express, I don't know how true this is though). Here's one small function in its most basic form that won't work for me:
public static HtmlElement GetDocumentControlByID(
ref SHDocVw.InternetExplorer IEObj,
string ControlID)
{
HtmlElement ReturnElement = IEObj.Document.GetElementById(ControlID);
return ReturnElement;
}
The problem is that when I create the IEObj instance, it is considered type System.__ComObject instead of SHDocVw.InternetExplorer, and all subparts are also of type System.__ComObject. When I try any of the following statements...
Document WebDoc = IEObj.Document;
HtmlElement ReturnElement = IEObj.Document.GetElementById(ControlID);
...I keep getting the same error message:
Cannot implicitly convert type 'System.__ComObject' to 'System.Windows.Forms.HtmlElement'
(obviously the convert-to type is different for the IEObj.Document).
I am new to c# (coming from VBA, so I am familiar with programming), but in VBA, the equivelant works perfectly without needing to convert it in any way.
Is it something I'm doing wrong? In case it's my creation of the object, the following is (roughly) the code I used to test the function:
public static void Main(String [] args)
{
SHDocVw.InternetExplorer IEObj = new SHDocVw.InternetExplorer();
IEObj.Navigate("http://sports.ladbrokes.com/");
while (IEObj.ReadyState != 4)
{
}
// There is a textbox that definitely exists
HtmlElement NetControl = GetDocumentControlByID(ref IEObj, "username");
// I was goint to manipulate it after this, but it crashes in the above function.
}
All I really want to do is latch onto various elements so that I can enter text into textboxes, click buttons etc. I would also need to be able to use the Document variables as well (like Document.Body.InnerHtml, etc). The whole project is to be a bunch of functions to be contained in a DLL to be referenced by other projects.
You're trying to use the WinForms HtmlElement class, which isn't a COM object.
You can't mix the native InternetExplorer COM object with the managed classes in WinForms.
You should use the WinForms classes (the WebBrowser control) instead.
In most cases, you don't need COM at all.