I instantiate a COM Object, then invoke a method.
ActiveXComponent comp = new ActiveXComponent("MyDll.MyClass");
String argument1 = "test1";
String argument2 = "test2";
Variant[] arguments = { new Variant(argument1), new Variant(argument2) };
comp.invoke("myMethod", arguments)
Assuming MyDll has a method called
myMethod(String s1, String s2)
it works fine.
Now, what if I have a Method
myMethod(String s1, ReturnDeletedModeEnum enum)
with an enum defined in MyDll?
I need to pass the enum to the method somehow, but I don't know how to access it.
I tried getting the Enum as ActiveXComponent,
new ActiveXComponent("MyDll.ReturnDeletedModeEnum");
which (not surprisingly) didn't work:
com.jacob.com.ComFailException: Can't get object clsid from progid
I tried finding some more documentation about Jacob, because there seem to be Enum-specific classes, but I haven't found any explanation on how to use them.
I ran into the same uncertainty when I needed to call a method with an Enummeration parameter. I couldn't find much documentation - JACOB or otherwise.
I did stumble across a helpful post on the subject which says the values ... correspond to internally stored numbers and An enumeration in VBA is always of data type Long.
Armed with that and the MS Documentation for my particular Enumeration, I gave this a try ...
Dispatch.call(oDocuments, "Open", fileIn, ... , new Variant(1L));
And it worked!
I'm sure there's a way to get actual "Enumeration" data structures, but this was good enough for me.
Related
I am using Jacob to call VBA COM interfaces in software.
I need to call a Sub that has an output parameter, that is not of canonical type (int, String, etc.) but of some dedicated interface declared in .tlb that are brought with this software.
Here is the
Sub GetMaterialOnBody (Body iBody, Material oMaterial)
So I tried many declarations and initializations for the output parameter material in the call, I get various errors and cannot seem to find the proper way to do.
Variant material = new Variant (null, false);
Dispatch.invoke(materialManager.getDispatch(), "GetMaterialOnBody", Dispatch.Method, new Object[] {hybridBody, material}, new int[1]);
but got
com.jacob.com.ComFailException: A COM exception has been encountered:
At Invoke of: GetMaterialOnBody
Description: Type mismatch.
Then I tried to call .getDispatch() on material
Variant material = new Variant (null, false);
Dispatch.invoke(materialManager.getDispatch(), "GetMaterialOnBody", Dispatch.Method, new Object[] {hybridBody, material.getDispatch()}, new int[1]);
but got
java.lang.IllegalStateException: getDispatch() only legal on Variants of type VariantDispatch, not 0
So I tried
Variant material = new Variant (null, false);
material.putNothing();
Dispatch.invoke(materialManager.getDispatch(), "GetMaterialOnBody", Dispatch.Method, new Object[] {hybridBody, material.getDispatch()}, new int[1]);
but got
com.jacob.com.ComFailException: putObject failed
at com.jacob.com.Variant.putVariantDispatch(Native Method) ~[jacob-1.14.3.jar:na]
at com.jacob.com.Variant.putDispatch(Variant.java:1341) ~[jacob-1.14.3.jar:na]
I tried different solutions, including using Ref, etc.
I am a bit lost on how exactly we have to initialize a variant/dispatch to pass as an output parameter in a Sub.
Does anyone have any clue on how to do that? The closes resources I found were handling String/Integer and not object.
This old question might be related (but obviously no answer): https://community.oracle.com/tech/developers/discussion/1548970/jacob-out-parameters-refs-in-jni
I have open a question on the jacob github
https://github.com/freemansoft/jacob-project/issues/23
Also I emailed the owner of the project on github and he said the project was dormant for some time already. As all the code is available I will take the time to compile and debug the native (C++) side in order to debug my case. I will update that question then.
if that is output parameter, shouldn't it be 'ByRef' ?
Sub GetMaterialOnBody (Body iBody, ByRef Material oMaterial)
In your case probably GetMaterialOnBody sub is expected to fill in internals of oMaterial object, not to set reference to it? Then just create an empty Material object and pass to the sub
Trying to implement the Java SDK, following the tutorial here: https://blogs.msdn.microsoft.com/bharry/2011/05/16/announcing-a-java-sdk-for-tfs/
I have the libraries imported, but getting a compilation error
TFSTeamProjectCollection tpc =
new TFSTeamProjectCollection(BASEURL);
BASEURL is a string I defined earlier in the code.
The error is: The constructor TFSTeamProjectCollection(String) is undefined
Does anyone know how to resolve this issue?
Well, studying the C# side of things ( see here ) I can't find a constructor that would only take a String argument.
There is only a single argument constructor taking a Uri.
In other words: the fact that you your string contains a Url; and that you named it a BASEURL doesn't magically turn it from a String into a URL or URI class object.
Guessing: the ctor wants an argument of type java.net.URL which you could probably create with something like new URL(BASEURL); instead of just passing BASEURL to that constructor.
The thing is: in order to actually understand which constructors that Team Foundation class has; one would need access to the corresponding SDK from Microsoft - which you probably downloaded and and have in place. So, the only thing that you need to do ... read the javadocs!
When I try to invoke the .NET method – ‘Create’ from Java using Javonet I get a message the method does not exist because I am not passing the correct parameters –
DocuWare.Platform.ServerClient.ServiceConnection
Create(System.Uri,
System.String,
System.String,
System.String,
System.Nullable`1[DocuWare.Platform.ServerClient.DWProductTypes],
System.Net.Http.HttpMessageHandler,
System.Net.Http.Headers.ProductInfoHeaderValue[]
)
My code is –
NObject objUri = Javonet.New("Uri","http://<IP-address>/DocuWare/Platform");
NType serviceConnectionClass = Javonet.getType("DocuWare.Platform.ServerClient.ServiceConnection");
NObject objProductInfoHeaderValue = Javonet.New("System.Net.Http.Headers.ProductInfoHeaderValue","DocuWare+.NET+API+Test+Client", "1.0");
NObject[] objProductInfoHeaderValueArray = new NObject[] {objProductInfoHeaderValue};
NType typeHttpMessageHandler = Javonet.getType("System.Net.Http.HttpMessageHandler");
NType typeNullable = Javonet.getType("System.Nullable");
serviceConnectionClass.invoke("Create",objUri,"admin","admin","<company-name>",typeNullable,typeHttpMessageHandler,objProductInfoHeaderValueArray);
My main problem is not knowing how to generate ‘Nullable’ objects for –
DocuWare.Platform.ServerClient.DWProductTypes
System.Net.Http.HttpMessageHandler
System.Net.Http.Headers.ProductInfoHeaderValue[]
I don't think this is a problem with JavONet, but I need to get past this problem before I can perform a Proof-of-concept
Here is the link to the Docuware Platform –
http://help.docuware.com/sdk/platform-eagle/html/66b2ed1e-2aef-452a-97cd-5014bbf0242b.htm
I am running the Test using Tomcat app server and JSP. I know the .NET .dll are being found and the Javonet library is being correctly activated.
Thanks in advance for any help.
Normally for nullable arguments you can pass either the regular target type in your case some enum value of "DWProductTypes" or if you want to pass null just pass the "null";
So you should make the call:
serviceConnectionClass.invoke("Create",objUri,"admin", "admin","Intermodal Tank",new NEnum("DWProductTypes","DocuWareClient"),typeHttpMessageHandler,objProductInfoHeaderValueArray);
All possible values for DWProductTypes you can find here:
http://help.docuware.com/sdk/platform/html/T_DocuWare_Platform_ServerClient_DWProductTypes.htm
Here you find more about using enumerations:
https://www.javonet.com/quick-start-guide/#Enums_How_To_Work_With_Enums
or pass null:
serviceConnectionClass.invoke("Create",objUri,"admin", "admin","Intermodal Tank",null,typeHttpMessageHandler,objProductInfoHeaderValueArray);
For argument HttpMessageHandler just create the instance:
NObject typeHttpMessageHandler = Javonet.New("SomeConcreteTypeInheritingFromHttpMessageHandler");
For ProductInfoHeaderValue array you should create Java array of NObjects and pass it as argument:
NObject[] objProductInfoHeaderValueArray = new NObject[1];
objProductInfoHeaderValueArray[0] = Javonet.New("ProductInfoHeaderValue","productName","version");
My program has two parts, one data structure and second operations.
Data Structure part is, basically and object & set of that object. This set is a class that extends ArrayList. Set and Objects are unique, what I mean is if I have a class named A then I have aSet which keeps the all A objects in it.
Operation part is doing some operations on those sets and most of the times it is also creates a new class in the runtime (write the class in a java file and compile it then call it from Reflect library functions e.g. constructor.newInstance(args);) and creates an object from new class then fill inside of it and return it. Since I didn't wanted to overwrite classes I use UUID trimmed for naming the new classes that created at the runtime.
I had noticed something odd. I run my code and it works, then I run it again and at the creation of set, it gives me "java.lang.IllegalArgumentException: wrong number of arguments". I run it again exception, again and again then I run it again then no exception. (Every time it created a new class like I wanted)
So why my code can act like this?
I am using Oracle Java 1.8 on Ubuntu 14.04 with Intellij Idea (tried also Eclipse no change).
Edit for code:
"I removed the codes unrelated to the problem"
And this is the part that gives error at my operation; (Initial innerInnerName is the random generated class name)
final String outerClassName = "" + initialInnerName + "Table";
final String innerClassName = "" + initialInnerName;
File file = new File("./src/");
URL url = file.toURI().toURL();
URL urls[] = new URL[]{url};
URLClassLoader loader1 = new URLClassLoader(urls);
Class<?> innerClass = loader1.loadClass(innerClassName);
Set<Template> newTable = null;
Class<?> outerClass = loader1.loadClass(outerClassName);
Constructor<?> constructorOfOuterClass = outerClass.getDeclaredConstructors()[0];
Object[] objArg = {innerClass,10};
newTable = (Set<Template>) constructorOfOuterClass.newInstance(objArg);
Edit 2
Exception being thrown at last line of last code which is:
newTable = (Set<Template>) constructorOfOuterClass.newInstance(objArg);
Well I had tried to do MCVE as much as possible.
Your "Outer Class" has two constructors, one that takes one argument and one that takes two. When you go to instantiate the outer class, you get the first in the list of constructors:
Constructor<?> constructorOfOuterClass = outerClass.getDeclaredConstructors()[0];
and then you try to invoke it with two arguments:
Object[] objArg = {innerClass,10};
newTable = (Set<Template>) constructorOfOuterClass.newInstance(objArg);
The problem is that the constructors aren't necessarily in the order you expect. No particular order is guaranteed, so sometimes you will get the one argument constructor and sometimes the two argument constructor. When you get the two argument constructor, all is well. But when you get the one argument constructor and invoke it with two arguments, well, you know all too well what happens then.
You need to determine which of the constructors is the one you want, and select it, rather than assuming that it is the first one in the list.
The situation seems to be abnormal, but I was asked to build serializer that will parse an object into string by concatenating results of "get" methods. The values should appear in the same order as their "get" equivalent is declared in source code file.
So, for example, we have
Class testBean1{
public String getValue1(){
return "value1";
}
public String getValue2(){
return "value2";
}
}
The result should be:
"value1 - value2"
An not
"value2 - value1"
It can't be done with Class object according to the documentation. But I wonder if I can find this information in "*.class" file or is it lost? If such data exists, maybe, someone knows a ready to use tool for that purpose? If such information can't be found, please, suggest the most professional way of achieving the goal. I thought about adding some kind of custom annotations to the getters of the class that should be serialized.
If you want that you have to parse the source code, not the byte code.
There are a number of libraries that parse a source file into a node tree, my favorite is the javaparser (hosted at code.google.com), which, in a slightly modified version, is also used by spring roo.
On the usage page you can find some samples. Basically you will want to use a Visitor that listens for MethodDefinitions.
Although reflection does not anymore (as of java 7 I think) give you the methods in the order in which they appear in the source code, the class file appears to still (as of Java 8) contain the methods in the order in which they appear in the source code.
So, you can parse the class file looking for method names and then sort the methods based on the file offset in which each method was found.
If you want to do it in a less hacky way you can use Javassist, which will give you the line number of each declared method, so you can sort methods by line number.
I don't think the information is retained.
JAXB, for example, has #XmlType(propOrder="field1, field2") where you define the order of the fields when they are serialized to xml. You can implemenet something similar
Edit: This works only on concrete classes (the class to inspect has its own .class file). I changed the code below to reflect this. Until diving deeper into the ClassFileAnalyzer library to work with classes directly instead of reading them from a temporary file this limitation exists.
Following approach works for me:
Download and import following libarary ClassFileAnalyzer
Add the following two static methods (Attention! getClussDump() needs a little modification for writing out the class file to a temporary file: I removed my code here because it's very special at this point):
public static String getClassDump(Class<?> c) throws Exception {
String classFileName = c.getSimpleName() + ".class";
URL resource = c.getResource(classFileName);
if (resource == null) {
throw new RuntimeException("Works only for concreate classes!");
}
String absolutePath = ...; // write to temp file and get absolute path
ClassFile classFile = new ClassFile(absolutePath);
classFile.parse();
Info infos = new Info(classFile, absolutePath);
StringBuffer infoBuffer = infos.getInfos();
return infoBuffer.toString();
}
public static <S extends List<Method>> S sortMethodsBySourceOrder(Class<?> c, S methods) throws Exception {
String classDump = getClassDump(c);
int index = classDump.indexOf("constant_pool_count:");
final String dump = classDump.substring(index);
Collections.sort(methods, new Comparator<Method>() {
public int compare(Method o1, Method o2) {
Integer i1 = Integer.valueOf(dump.indexOf(" " + o1.getName() + lineSeparator));
Integer i2 = Integer.valueOf(dump.indexOf(" " + o2.getName() + lineSeparator));
return i1.compareTo(i2);
}});
return methods;
}
Now you can call the sortMethodsBySourceOrder with any List of methods (because sorting arrays is not very comfortable) and you will get the list back sorted.
It works by looking at the class dumps constant pool which in turn can be determined by the library.
Greetz,
GHad
Write your custom annotation to store ordering data, then use Method.getAnnotation(Class annotationClass)