I need to write an application for a client that calls a method from a ".dll" file. The ".dll" file was previously executed manually from an ".exe" GUI but now they want to automate the process.
I never worked with .dll files so everything that I found until now is the result of a complete day of research, I also received a small documentation with this tool:
The interface is an ActiveX DLL which provides two functions (GetUnitInfo and SaveResult).
In the moment I just want to run the "GetUnitInfo" method from the Winwdows command line using RUNDLL32.exe.
This is the documentation for the "GetUnitInfo" method:
The interface for GetUnitInfo is as follows:
Public Function GetUnitInfo( _
ByVal strRequest As String, _
ByRef strUnitInfo As String,
Optional ByVal strStationName As String = "") As Long
Sample calling code can be:
Dim lRet As Long
Dim strXML as String
lRet = GetUnitInfo( _“<?xml version=""1.0"" ?><GetUnitInfo
xmlns=""urn:GetUnitInfo-schema"" SerialNumber=""BD3ZZTC8MA"" />", strXML)
So I tried to run this method with some dummy parameters because the method returns an error if the parameters are not OK. The command:
RUNDLL32.EXE FFTester.dll, GetUnitInfo test1, test2
But I receive this error:
I used "Dependency Walker" to list the functions from the dll file:
But this are all the functions, normally I would expected that also "GetUnitInfo" is listed.
Can somebody help? It is not mandatory to use RUNDLL32.
Later edit:
I want to call this DLL from a tool that is written in JAVA, I tried to use JNA but I failed so I was thinking to call the dll functions from the command line because if this works I can use a process builder to execute the command.
I fixed my problem and I will provide a solution, maybe it will help someone else.
I used com4j library to generate the interfaces for my dll. After this you need to register your DLL otherwise most problely your code will throw an "ComException", you can read more in my second question.
To register a DLL:
C:\Windows\SysWOW64>regsvr32.exe "path to your DLL" for 32 bit DLL
Or
C:\Windows\System32>regsvr32.exe "path to your DLL" for 64 bit DLL
Also depending on your DLL type, 32 or 64 bit, you need to use proper Eclipse/JDK.
Related
I am trying to use py4j to open up a gateway that I can use to pass objects from java into python. When I try to open a gateway with the py4j function launch_gateway it does not seem to properly connect to my Java class. However, when I launch my java class in the command line and then connect to it in python using JavaGateway everything works as expected. I would like to be able to use the built in method as I am sure that I am not accounting for things that have already been considered in the design of py4j, but I'm just not sure what I'm doing wrong.
Let's say I wanted to create a gateway to the class sandbox.demo.solver.UtilityReporterEntryPoint.class. In the command line I can do this by executing the following:
java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer
This launches as expected and I can use the methods in my class from within python after connecting to the gateway. So far so good.
My understanding of the py4j documentation would lead me to believe I should do the following to launch the gateway in python:
port = launch_gateway(classpath='sandbox.demo.solver.UtilityReporterEntryPoint')
params = GatewayParameters(port=port)
gateway= JavaGateway(gateway_parameters=params)
I get no errors when executing these three lines, but when I try to access my java class methods with gateway.entry_point.someMethod() it fails with the following error:
Py4JError: An error occurred while calling t.getReport. Trace:
py4j.Py4JException: Target Object ID does not exist for this gateway :t
at py4j.Gateway.invoke(Gateway.java:277)
at py4j.commands.AbstractCommand.invokeMethod(AbstractCommand.java:132)
at py4j.commands.CallCommand.execute(CallCommand.java:79)
at py4j.GatewayConnection.run(GatewayConnection.java:214)
at java.lang.Thread.run(Thread.java:745)
Obviously something is not getting called correctly within launch_gateway or I am feeding it the wrong information.
In the py4j source code for launch_gateway you can see that given the inputs you provide and those constructed by the function, a command is constructed that eventually gets called by subprocess.Popen. So given the input passed to launch_gateway above the command passed into Popen would be:
command = ['java', '-classpath', '/Users/grr/anaconda/share/py4j/py4j0.10.4.jar:sandbox.demo.solver.UtilityReporterEntryPoint', 'py4j.GatewayServer', '0']
Passing this command to Popen returns the listening port as expected. However, connecting to this listening port still does not allow access to my class methods.
Finally, passing the command as a single string to Popen without the final argument ('0'), properly launches a gateway which again operates as expected. Having taken a glance at the Java source code for py4j.GatewayServer.class this makes no sense as the main method seems to indicate that the class should exit with status 1 if the length of arguments is 0.
At this point I'm kind of at a loss. I can hack my way into a workable solution, but as I said I'm sure that ignores important aspects of the gateway behavior and I don't like hacky solutions. I'd love to tag #Barthelemy in this one, but hopefully he reads this. Thanks in advance for any help.
EDIT
For now I have been able to work around this issue with the following steps.
Package entire project including all external dependencies into a single jar file magABM-all.jar, with 'Main-Class' set to UtilityReporterEntryPoint.
Include if...else block regarding presence of --die-on-exit exactly like it is in GatewayServer.java
Use subprocess.Popen to call the command to run the project jar.
UtilityReporterEntryPoint.java
public static void main(String[] args) throws IOException {
GatewayServer server = new GatewayServer(new UtilityReporterEntryPoint());
System.out.println("Gateway Server Started");
server.start();
if (args[0].equals("--die-on-exit")) {
try {
BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in, Charset.forName("UTF-8")));
stdin.readLine();
System.exit(0);
} catch (java.io.IOException e) {
System.exit(1);
}
}
}
app.py
def setup_gateway()
"""Launch a py4j gateway using UtilityReporterEntryPoint."""
process = subprocess.Popen('java -jar magABM-all.jar --die-on-exit', shell=True)
time.sleep(0.5)
gateway = JavaGateway()
return gateway
In this way I can still use gateway.shutdown if necessary and if the python process that starts the py4j gateway dies or is closed the gateway will be closed.
N.B I would by no means consider this a final solution as py4j was written by much smarter individuals with a clear purpose in mind and I am sure that there is a way to manage this exact workflow within the confines of py4j. This is just a stopgap solution.
There are a few issues:
The classpath parameter in launch_gateway should be a directory or a jar file, not a class name. For example, if you want to include additional Java libraries, you would add them to the classpath parameter.
The error you receive when you call gateway.entry_point.someMethod() means that you have no entry point. When you call launch_gateway, the JVM is started with GatewayServer.main, which launches a GatewayServer with no entry point: GatewayServer server = new GatewayServer(null, port). It is not possible currently to use launch_gateway and specify an entry point.
When you start the JVM with java -cp /Users/grr/anaconda/share/py4j/py4j0.10.4.jar: sandbox.demo.solver.UtilityReporterEntryPoint py4j.GatewayServer I believe the JVM uses UtilityReporterEntryPoint as the main class. Although you did not provide the code, I assume that this class has a main method and that it launches a GatewayServer with an instance of UtilityReporterEntryPoint as the entry point. Note that there is a whitespace between the colon and the class name so UtilityReporterEntryPoint is seen as the main class and not as being part of the classpath.
I have a simple python script in my local machine, which returns a string. I want to run this script from java application and get the return value. I'm trying to do this using Pyrolite. I downloaded the jar files and added them to my java class path. But I'm not able to run the script.
I got the below sample code from readme.txt
NameServerProxy ns = NameServerProxy.locateNS(null);
PyroProxy remoteobject = new PyroProxy(ns.lookup("Your.Pyro.Object"));
Object result = remoteobject.call("pythonmethod", 42, "hello", new int[]{1,2,3});
String message = (String)result; // cast to the type that 'pythonmethod' returns
System.out.println("result message="+message);
remoteobject.close();
ns.close();
But this is not working for me. My system configuration is
OS: Windows 8
JDK: jdk1.7.0_51
Python: 2.6
Please help me with this.
This is how I have edited the code:
NameServerProxy ns = NameServerProxy.locateNS(null);
PyroProxy remoteobject = new PyroProxy();
Object result = remoteobject.call("C:\\trail1.py", null);
String message = (String)result; // cast to the type that 'pythonmethod' returns
System.out.println("result message="+message);
remoteobject.close();
ns.close();
I'm not positive that I understand what you're trying to do.
If you carefully read the tutorial, you'll see that you can't use Pyrolite the way you are. It specifies that you must have a python script running as a server, WITH a name server, where you must define some classes (for example Your.Pyro.Object).
Then you'll be able to call those objects you defined in that python script, but not the script itself.
To do what you want to do you'll need to call a function like C's fork(). Then you're able to call an executable, and you don't need Pyrolite.
I've been struggling with this problem for two days now and no resource I've found have been able to solve it.
I am trying to call a java class (added the link at the bottom) from Matlab (version 7.13.0.564 (R2011b)). I've compiled the java class using java 1.6 into a .class file and also added the path to the folder where the file is situated using javaaddpath (I've of course checked that the path is correct in the list of dynamic paths). However, when I try to call the class from Matlab using javaMethod('main','PerlinNoiseGenerator','') I get the error:
"No class PerlinNoiseGenerator can be located on Java class path"
I would be extremely grateful if someone with experience in calling java from Matlab could put together a short tut on how to do this. I am probably going to distribute my code so I kinda need to set the java path dynamically and from what I've read it really should be possible although I've seen post that indicate that it could be the cause of the problem.
http://svn.j3d.org/code/tags/Xj3D-M10/src/java/org/j3d/texture/procedural/PerlinNoiseGenerator.java
Usually I create jar files that contain java classes. I also had problems loading individual java classes before. In your case I did the following on xubuntu 13.04 x64 and Matlab 2013a x64 to load your particular class:
Compile it using java 6 (not the default 7) with option -d . to create a set of package folders, as your class defines a package org/j3d/texture/proecedural/ etc:
/usr/lib/jvm/java-6-openjdk-amd64/bin/javac -d . PerlinNoiseGenerator.java
This will compile the class and make in the current director (thus .) the set of package folders.
Make jar file containing your class again using jar from java 6. I named it javaNoise.jar:
/usr/lib/jvm/java-6-openjdk-amd64/bin/jar cf javaNoise.jar ./org/j3d/texture/procedural/PerlinNoiseGenerator.class
In Matlab, in the directory where javaNoise.jar is:
javaaddpath('./javaNoise.jar');
Create object of your java class:
png=org.j3d.texture.procedural.PerlinNoiseGenerator()
% results in: png = org.j3d.texture.procedural.PerlinNoiseGenerator#3982a033
To test it, I just generated some 1D noise:
png.noise1(1.2)
ans = -0.0960
Hope this helps.
P.S.
javaMethod('main','PerlinNoiseGenerator','') wont work because this class has no main method:-).
Your notation to the compiler of the constructor is a polymorphic class meaning "use appropriate constructor that is called at runtime".
public PerlinNoiseGenerator()
public PerlinNoiseGenerator(int seed)
The first form with no argument can be called but is irrelevent because the line with this(DEFAULT_SEED) attempts to call itself but only one constructor is allowed used
Second constructor has int for an argument but requires being loaded by an already loaded class.
Use the first version and change the case sensitive name of the one with the argument and remove this(DEFAULT_SEED) from it replace with the method name(the one you changed from a constructor that has the argument).
e.g. public perlinNoiseGenerator(int seed)
note: by convention java code method names start with a lower-case letter.
A final note, java arguments from the command line come in as "String" data type through the "main" method, a starter method for applications (gui or command prompt).
The first argument on the main method argument is the first commandline argument.
public static void main(String[] Args){
new PerlinNoiseGenerator(Args); // recursive class call
}//end main method
int[] args; // global
public PerlinNoiseGenerator(String[] Args){
int arglength=Args.length();
args = new int[arglength];
for(int cnt=0;cnt<arglength;cnt++){
Args[cnt].trim();
args[cnt]=new Integer(Args[cnt]).intValue();
}//enfor
perlinNoiseGenerator(args[0]); // call method
}//end constructor
I am writing a trading program in perl with the newest Finance::InteractiveBrokers::TWS
module. I kick off a command line interface in a separate thread at the
beginning of my program but then when I try to create a tws object, my program
exits with this message:
As of Inline v0.30, use of the Inline::Config module is no longer supported or
allowed. If Inline::Config exists on your system, it can be removed. See the
Inline documentation for information on how to configure Inline. (You should
find it much more straightforward than Inline::Config :-)
I have the newest versions of Inline and Inline::Java. I looked at TWS.pm and it doesn't seem to be using Inline::Config. I set 'SHARED_JVM => 1' in the 'use Inline()' and 'Inline->bind()' calls in TWS.pm but that did not resolve the issue...
My Code:
use Finance::InteractiveBrokers::TWS;
use threads;
use threads::shared;
our $callback;
our $tws;
my $interface = UserInterface->new();
share($interface);
my $t = threads->create(sub{$interface->runUI()});
$callback= TWScallback->new();
$tws = Finance::InteractiveBrokers::TWS->new($manager); #This is where the program fails
So is Inline::Config installed on your system or not? A cursory inspection of the code is not sufficient to tell whether Perl is loading a module or not. There are too many esoteric ways (some intentional and some otherwise) to load a package or otherwise populate a namespace.
The error message in question comes from this line of code in Inline.pm:
croak M14_usage_Config() if %main::Inline::Config::;
so something in your program is populating the Inline::Config namespace. You should do what the program instructs you to do: find out where Inline/Config.pm is installed on your system (somewhere in your #INC path) and delete it.
I use Launch4j as a wrapper for my Java application under Windows 7, which, to my understanding, in essence forks an instance of javaw.exe that in turn interprets the Java code. As a result, when attempting to pin my application to the task bar, Windows instead pins javaw.exe. Without the required command line, my application will then not run.
As you can see, Windows also does not realize that Java is the host application: the application itself is described as "Java(TM) Platform SE binary".
I have tried altering the registry key HKEY_CLASSES_ROOT\Applications\javaw.exe to add the value IsHostApp. This alters the behavior by disabling pinning of my application altogether; clearly not what I want.
After reading about how Windows interprets instances of a single application (and a phenomenon discussed in this question), I became interested in embedding a Application User Model ID (AppUserModelID) into my Java application.
I believe that I can resolve this by passing a unique AppUserModelID to Windows. There is a shell32 method for this, SetCurrentProcessExplicitAppUserModelID. Following Gregory Pakosz suggestion, I implemented it in an attempt to have my application recognized as a separate instance of javaw.exe:
NativeLibrary lib;
try {
lib = NativeLibrary.getInstance("shell32");
} catch (Error e) {
Logger.out.error("Could not load Shell32 library.");
return;
}
Object[] args = { "Vendor.MyJavaApplication" };
String functionName = "SetCurrentProcessExplicitAppUserModelID";
try {
Function function = lib.getFunction(functionName);
int ret = function.invokeInt(args);
if (ret != 0) {
Logger.out.error(function.getName() + " returned error code "
+ ret + ".");
}
} catch (UnsatisfiedLinkError e) {
Logger.out.error(functionName + " was not found in "
+ lib.getFile().getName() + ".");
// Function not supported
}
This appears to have no effect, but the function returns without error. Diagnosing why is something of a mystery to me. Any suggestions?
Working implementation
The final implementation that worked is the answer to my follow-up question concerning how to pass the AppID using JNA.
I had awarded the bounty to Gregory Pakosz' brilliant answer for JNI that set me on the right track.
For reference, I believe using this technique opens the possibility of using any of the APIs discussed in this article in a Java application.
I don't have Windows 7 but here is something that might get you started:
On the Java side:
package com.stackoverflow.homework;
public class MyApplication
{
static native boolean setAppUserModelID();
static
{
System.loadLibrary("MyApplicationJNI");
setAppUserModelID();
}
}
And on the native side, in the source code of the `MyApplicationJNI.dll library:
JNIEXPORT jboolean JNICALL Java_com_stackoverflow_homework_MyApplication_setAppUserModelID(JNIEnv* env)
{
LPCWSTR id = L"com.stackoverflow.homework.MyApplication";
HRESULT hr = SetCurrentProcessExplicitAppUserModelID(id);
return hr == S_OK;
}
Your question explicitly asked for a JNI solution. However, since your application doesn't need any other native method, jna is another solution which will save you from writing native code just for the sake of forwarding to the windows api. If you decide to go jna, pay attention to the fact that SetCurrentProcessExplicitAppUserModelID() is expecting a UTF-16 string.
When it works in your sandbox, the next step is to add operating system detection in your application as SetCurrentProcessExplicitAppUserModelID() is obviously only available in Windows 7:
you may do that from the Java side by checking that System.getProperty("os.name"); returns "Windows 7".
if you build from the little JNI snippet I gave, you can enhance it by dynamically loading the shell32.dll library using LoadLibrary then getting back the SetCurrentProcessExplicitAppUserModelID function pointer using GetProcAddress. If GetProcAddress returns NULL, it means the symbol is not present in shell32 hence it's not Windows 7.
EDIT: JNA Solution.
References:
The JNI book for more JNI examples
Java Native Access (JNA)
There is a Java library providing the new Windows 7 features for Java. It's called J7Goodies by Strix Code. Applications using it can be properly pinned to the Windows 7 taskbar. You can also create your own jump lists, etc.
I have implemented access to the SetCurrentProcessExplicitAppUserModelID method using JNA and it works quite well when used as the MSDN documentation suggests. I've never used the JNA api in the way you have in your code snippet. My implementation follows the typical JNA usage instead.
First the Shell32 interface definition:
interface Shell32 extends StdCallLibrary {
int SetCurrentProcessExplicitAppUserModelID( WString appID );
}
Then using JNA to load Shell32 and call the function:
final Map<String, Object> WIN32API_OPTIONS = new HashMap<String, Object>() {
{
put(Library.OPTION_FUNCTION_MAPPER, W32APIFunctionMapper.UNICODE);
put(Library.OPTION_TYPE_MAPPER, W32APITypeMapper.UNICODE);
}
};
Shell32 shell32 = (Shell32) Native.loadLibrary("shell32", Shell32.class,
WIN32API_OPTIONS);
WString wAppId = new WString( "Vendor.MyJavaApplication" );
shell32.SetCurrentProcessExplicitAppUserModelID( wAppId );
Many of the API's in the last article you mentioned make use of Windows COM which is quite difficult to use directly with JNA. I have had some success creating a custom DLL to call these API's (eg. using the SHGetPropertyStoreForWindow to set a different app ID for a submodule window) which I then use JNA to access at runtime.
Try to use JSmooth. I use always this one. In JSmooth is there an option under Skeleton by Windowed Wrapper called
Lauch java app in exe process
See on this image.
(source: andrels.com)
Also command line arguments can be passed.
I think this can be a solution for you.
Martijn
SetCurrentProcessExplicitAppUserModelID (or SetAppID()) would in fact do what you're trying to do. However, it might be easier to modify your installer to set the AppUserModel.ID property on your shortcut - quoting from the Application User Model ID document mentioned above:
In the System.AppUserModel.ID property of the application's shortcut file. A shortcut (as an IShellLink, CLSID_ShellLink, or a .lnk file) supports properties through IPropertyStore and other property-setting mechanisms used throughout the Shell. This allows the taskbar to identify the proper shortcut to pin and ensures that windows belonging to the process are appropriately associated with that taskbar button.
Note: The System.AppUserModel.ID property should be applied to a shortcut when that shortcut is created. When using the Microsoft Windows Installer (MSI) to install the application, the MsiShortcutProperty table allows the AppUserModelID to be applied to the shortcut when it is created during installation.
The latest jna-platform library now includes JNA bindings for SetCurrentProcessExplicitAppUserModelID:
https://github.com/java-native-access/jna/pull/680
I fixed mine without any ID settings.
There is an option in Launch4J if you are using it and you say you do then...
You can change the header to JNI Gui and then wrap it around the jar with the JRE.
The good thing is that it runs .exe in the process now instead on running javaw.exe with your jar. It probably does it under the hood (not sure).
Also I have noticed also that it takes around 40-50% less CPU resource which is even better!
And the pinning works fine and all that window features are enabled.
I hope it helps to someone as I spent nearly 2 days trying to solve that issue with my undecorated javafx app.