Our application involves an external device that mounts as a USB mass storage device. I need to be able to check the integrity of that USB mass storage device from a Java application, running on Windows.
Currently I execute "chkdsk", which works fine unless the user's computer isn't configured for English. (Because I need to examine the chkdsk output to determine the state of the drive.) And, surprise, surprise, not every computer in the world is configured for English!
There is a Win32 class called "Win32_Volume" with a method called "Chkdsk" that is just what I'm looking for.
I need to call it from Java, and JNA seems the way to go.
How can I use COM to call a method from a Win32 WMI class?
You can send an FSCTL_IS_VOLUME_DIRTY message using DeviceIoControl.
Here is and example of calling DeviceIoControl.
Or you can use WMI, both the the Win32_Volume and the Win32_LogicalDisk class expose a DirtyBitSet property. However WMI is really inteded for scripting languages and is not preferred because of it's incredible performance overhead.
The Win32_Volume documentation with the Chkdsk method you cite indicates it is a legacy API, but has not yet been deprecated so you could use it.
The current Storage Management API equivalent is MSFT_Volume. It has a Diagnose method that looks to perform similar functionality.
To execute a method on a WMI object via COM you need to obtain a pointer to a WbemServices object. It has an ExecMethod method you can invoke that does what you want for WMI classes. The code to retrieve this object is in JNA's WbemcliUtil class as a return type from the connectServer() method.
You will also need the full path to the Class Object; this is available by querying WMI for the __PATH, similarly to how you'd query any other field. That same WbemcliUtil class has a WmiQuery class you can instantiate to execute the query, collecting that path from the WmiResult. See the JNA test classes for examples on using these.
Finally, you can execute the WMI method. The code below will accomplish it for String properties, e.g., the MSFT_StorageDiagnoseResult object you'd get from MSFT_Volume's Diagnose method. You'd have to treat other return types differently.
/**
* Convenience method for executing WMI methods without any input parameters
*
* #param svc
* The WbemServices object
* #param clsObj
* The full path to the class object to execute (result of WMI
* "__PATH" query)
* #param method
* The name of the method to execute
* #param properties
* One or more properties returned as a result of the query
* #return An array of the properties returned from the method
*/
private static String[] execMethod(WbemServices svc, String clsObj, String method, String... properties) {
List<String> result = new ArrayList<>();
PointerByReference ppOutParams = new PointerByReference();
HRESULT hres = svc.ExecMethod(new BSTR(clsObj), new BSTR(method), new NativeLong(0L), null, null, ppOutParams,
null);
if (COMUtils.FAILED(hres)) {
return new String[0];
}
WbemClassObject obj = new WbemClassObject(ppOutParams.getValue());
VARIANT.ByReference vtProp = new VARIANT.ByReference();
for (String prop : properties) {
hres = obj.Get(new BSTR(prop), new NativeLong(0L), vtProp, null, null);
if (!COMUtils.FAILED(hres)) {
result.add(vtProp.getValue() == null ? "" : vtProp.stringValue());
}
}
obj.Release();
return result.toArray(new String[result.size()]);
}
A more complete example of querying a COM object, where I extracted the above code, is in this utility class which queries the GetOwner() and GetOwnerSid() methods on the Win32_Process object. It's legacy code from before I contributed the WbemCli class to JNA, so you'll have to adapt some of it, but it should be a good starting point.
Related
Im answering this question below; I opened it because it is more general than what I originally asked in an earlier question, so it wouldn't really fit there. It took me quite some tinkering, so I thought I'd share the solution here.
My situation:
I use picoCLI to parse multiple configuration files that in turn can "include" other config files, to arbitrary depth. Unfortunately, for some of my options the order in which they are parsed does also matter.
In my application, there are "section" options like section=A:teacher that request section A and cause it to be processed (I'll leave out what that exactly means) for teachers, students or other groups. Among a number of other options, there is also one called configfile= that "includes" another option file. That situation can be described by a "tree" of configuration details:
# options given on actual command line ("root of tree")
section=A:teacher
configfile=cf-1.txt # include options from cf-1.txt
section=A:student # this indentation: options read from cf-1.txt
section=B:principal
configfile=cf-2.txt # read options from cf-2.txt
section=A:parent # this indentation: options read from cf-2.txt
section=C:parent
section=C:teacher # back in cf-1.txt
section=D:admin # back to actual command line
I want this tree to be traversed depth-first, with "later" options overwriting "earlier" ones if they refer to the same section name: In the end, section A should get parent and C should get teacher.
For parsing configfile= options, I can't use picoCLI's #-syntax because these files are not necessarily in the "current" folder, so I want to control where the application looks for them. That's problem #1. It is solved by the parseConfigfile method listed below.
Unfortunately, picoCLI has a peculiar quirk when an option occurs multiple times in the same file (as section does with A, B and C): It does call the annotated setter method each time but with accumulating option values in the list parameter of that method. The first call only gets (A:student), the second (A:student,B:prof), the third (A:student,B:prof,C:teacher) etc.
I learned here that this behaviour is intended but for me it is problem #2 because the repeated evaluation of section=A:student messes up my later-options-overwrite-earlier-ones semantics: In the end, A is incorrectly configured for teacher. For many options (those with "one-dimensional" values), that's not a problem, but it is for section= and, somewhat ironically, also for configfile=.
Here's my solution; maybe it's useful to someone. The basic approach is to use a new CommandLine instance for each nested config file.
All code snippets below are from the annotated class; project-specific housekeeping, error checking, path construction etc. were removed.
Problem #1 is solved by the parseConfigFile(...) method:
#Option(names = { "configfile", "cf" } )
public void parseConfigfile(final List<String> accumulatedCfgFiles) {
// prune the list, keeping only "new" entries that haven't been seen yet:
List<String> newCfgFiles = this.cfgfileHelper.retainNewOptionValuesOnly(
accumulatedCfgFiles, "configfile");
if(newCfgFiles.isEmpty()) {
// another picoCLI quirk: this happens even if there always are values
return;
} else if(newCfgFiles.size() > 1) {
// loop over the files if you want to allow this, or report an error
}
// some path tinkering left out
File cfgFile = new File(newCfgFiles.get(0));
if(this.stackOfConfigFiles.contains(cfgFile)) {
// report error because of cyclic reference
} else {
this.stackOfConfigFiles.push(cfgFile);
// task a new CommandLine instance with processing that file:
CommandLine cmd = new CommandLine(this);
String[] optionsFromFile = FileUtils.readLines(cfgFile); // Apache Commons
this.cfgfileHelper.wrapParseArgsCall(cmd, optionsFromFile);
this.stackOfConfigFiles.pop();
}
}
The method uses an instance of NestedCfgfileHelper (see source below) which does all the nested-config-specific housekeeping to solve problem #2. Your annotated class needs one helper instance as a public attribute. Call the constructor with the names of all "problem" options (those that the helper class should take care of):
...
public final NestedCfgfileHelper cfgfileHelper =
new NestedCfgfileHelper(new String[] { "configfile", "section" });
...
The following steps make all this work:
Identify those options that are sensitive to "spurious setter method calls" (most are not);
If there are any, paste NestedCfgfileHelper's source to your annotated class as an inner class;
Create an instance of NestedCfgfileHelper as a public member of your annotated class, telling the constructor the names of all those "problematic" options;
Never call yourInstanceOfCommandLine.parseArgs(...) directly. Instead, instantiate and initialize it, but pass it to the helper using instanceOfYourAnnotatedClass.cfgfileHelper.wrapParseArgs(...)
Let the setter method(s) for those "difficult" options...
... first get rid of "old" values from previous invocations by calling retainNewOptionValuesOnly, passing the name of the option;
... then process the remaining option value(s) normally.
Finally, here's the source of NestedCfgfileHelper:
/** NestedCfgfileHelper ensures that the values of certain options are
* processed just once, despite the picoCLI quirk. */
public final class NestedCfgfileHelper {
/** Maps an (option name|CommandLine instance) pair to the number of
* option values that instance has so far passed for that option.
* Because Java doesn't have Maps with two keys, it's implemented as
* a Map of Maps: */
private Map<String, Map<CommandLine, Integer>> mapOptionAndCLToCount =
new HashMap<>();
/** Constructs a helper instance and prepares it to handle the options
* given as parameters.
*
* #param optionNames any number of Strings, with each String denoting
* one option whose values should be protected against being processed
* multiple times */
public NestedCfgfileHelper(String... optionNames) {
// make one mapping for each option name given:
for(String optionName: optionNames) {
mapOptionAndCLToCount.put(optionName, new HashMap<CommandLine, Integer>());
}
}
/** This stack keeps track of CommandLine instances that are currently
* working on this annotated class instance. A stack is needed because
* config files can be nested. */
private Stack<CommandLine> stackOfCmdLineInstances = new Stack<>();
/** Wraps the call to {#link CommandLine#parseArgs(String...)} with some
* housekeeping so that when an annotated setter method is being called
* during option parsing, the helper method can look up from which
* CommandLine instance the call is coming.
* Because parseArg invocations will be nested recursively for nested config
* files, the respective CommandLine instances are kept on a stack.
* #param cl CommandLine instance that's been about to start parsing
* #param args options that are to be parsed */
public void wrapParseArgsCall(final CommandLine cl, final String[] args) {
// the brand new CommandLine instance hasn't passed any values yet,
// so put 0 in all maps:
mapOptionAndCLToCount.forEach(
(String s, Map<CommandLine, Integer> m) -> m.put(cl, 0));
this.stackOfCmdLineInstances.push(cl);
cl.parseArgs(args);
this.stackOfCmdLineInstances.pop();
}
/** This method filters its list parameter, discarding the first n
* entries (assuming they've already been processed), where n is retrieved
* from a Map instance kept for each option name.
* As a side effect, the updated number of option values is stored.
* This method should be called exactly once per invocation of an annotated
* setter method, and only by setter methods whose options values shouldn't
* be set multiple times.
*
* #param accumulated List containing all values (old and new ones
* accumulated) of the option named in the other parameter.
* #param optionName describes the option that's being parsed.
* #return pruned list containing only the "new" values that haven't
* been seen before. */
private List<String> retainNewOptionValuesOnly(
final List<String> accumulated,
final String optionName) {
// get the CommandLine instance currently working on this TFConfig instance:
CommandLine currentCL = this.stackOfCmdLineInstances.peek();
// get the CommandLine->int map for the option name passed:
Map<CommandLine, Integer> map = mapOptionAndCLToCount.get(optionName);
if(map == null) {
throw new IllegalArgumentException("unknown option: " + optionName);
}
/* Find out how many option values it has already passed to the setter. */
int n = map.get(currentCL);
/* discard the first n entries (they have already been processed) of
* accumulated, keeping only the "new" ones: */
List<String> optionValuesNewThisTime =
accumulated.subList(n, accumulated.size());
// associate the new number of patterns with the current CommandLine:
int newNumber = n + optionValuesNewThisTime.size();
map.put(currentCL, newNumber);
return optionValuesNewThisTime;
}
}
By following the Java Quickstart example, I am able to create a new Google App Script project and retrieve the scriptId. Also, by referring to the Restful API document, the script should be able to be executed using Method: scripts.run. However, I don't know how to retrieve the return value using com.google.api.services.script.Script in Java.
I've tried:
Script scriptService = getScriptService();
Script.Scripts scripts = scriptService.scripts();
Script.Scripts.Run run = scripts.run(scriptId, request);
and decompiled run function:
public Script.Scripts.Run run(String var1, ExecutionRequest var2) throws IOException {
Script.Scripts.Run var3 = new Script.Scripts.Run(var1, var2);
Script.this.initialize(var3);
return var3;
}
The function doesn't return an ExecutionResponse object which I am looking for.
Per the REST API documentation, calling script.run does not immediately return an ExecutionResponse object, but an Operation object that may contain an ExecutionResponse:
{
"done": boolean,
// Union field result can be only one of the following:
"error": {
object(Status)
},
"response": object(ExecutionResponse)
,
// End of list of possible types for union field result.
}
If we look at the Java API Client library, we see that method Script.Script.run takes arguments of the script ID, and an ExecutionRequest, and then returns a Script.Script.Run request that must be .execute()d:
Create a request for the method "scripts.run". This request holds the parameters needed by the script server. After setting any optional parameters, call the AbstractGoogleClientRequest.execute() method to invoke the remote operation.
The request referred to by the quoted documentation is Script.Script.Run, and has methods like .setAccessToken() for additional configuration, and several execution methods like .execute() and .executeMedia() to actually submit the execution request and return the Operation.
When I was studying android source code, I noticed that the general class loader in app is an instance of PathClassLoader, and there is two constructors in this class. One is like:
public PathClassLoader(String dexPath, ClassLoader parent) {
super(dexPath, null, null, parent);
}
and the other is like:
public PathClassLoader(String dexPath, String libraryPath,
ClassLoader parent) {
super(dexPath, null, libraryPath, parent);
}
But I cannot find the call of the second constructor in the source code during the procedure of app launching. So where does the value of libraryPath param come from? As it is known that libraryPath refers to the list of directories containing native libraries, and is used for initializing the value of nativeLibraryDirectories, which is a field of DexPathList object. So if there is no call of the second constructor with three params, how can the value of nativeLibraryDirectories be initialized? Therefore how can an app find its native libraries?
Actually, I am wondering who determines the value of nativeLibraryDirectories?
Hope some one can guide me in this. Thanks a lot.
So where does the value of libraryPath param come from?
You can use Android Studio search to find it out. Perform "Find in Path", specifying the "Scope" parameter to directory of Android sources. As a text to find paste following regex expression:
new PathClassLoader\(\w+, \w+, \w+\)\;
This matches a call of the constructor with three params. Also, do not forget to check "Regular expression" checkbox:
Then in the preview tab you'll be able to see the results:
With the same technique you can find out who is calling PathClassLoaderFactory#createClassLoader() function:
In ZygoneInit.java you'll be able to locate following piece of code:
/**
* Creates a PathClassLoader for the system server. It also creates
* a shared namespace associated with the classloader to let it access
* platform-private native libraries.
*/
private static PathClassLoader createSystemServerClassLoader(String systemServerClasspath,
int targetSdkVersion) {
String librarySearchPath = System.getProperty("java.library.path");
return PathClassLoaderFactory.createClassLoader(systemServerClasspath,
librarySearchPath,
null /* libraryPermittedPath */,
ClassLoader.getSystemClassLoader(),
targetSdkVersion,
true /* isNamespaceShared */);
}
Now, back to your questions.
So if there is no call of the second constructor with three params...
There is, ZygoteInit#handleSystemServerProcess() calls createSystemServerClassLoader(), which would eventually call 3 args constructor of PathClassLoader.
Actually, I am wondering who determines the value of nativeLibraryDirectories?
As you can see from the code above, it defaults to system property "java.library.path".
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
We don’t allow questions seeking recommendations for books, tools, software libraries, and more. You can edit the question so it can be answered with facts and citations.
Closed 4 years ago.
Improve this question
I am looking for a way to analyze user agent strings to determine whether they were generated by mobile devices. This needs to be java-based and usable in large batch log file analysis on hadoop for generating statistics (i.e., a web service wouldn't be appropriate).
I have seen WURFL, but given that I just need a binary mobile/not mobile response, the license fee seems prohibitive.
So far I have been using UADetector, which is almost exactly what I need. However, I have encountered some limitations with it. In my testing, I have found many user agent strings that provide enough information to determine that the user agent is from a mobile device, but are reported as UNKNOWN by UADetector.
For example, poorly-standardized Android apps can send the UA string "Android". This is enough to know that it came from a mobile device, but UADetector reports this UserAgentType as UNKNOWN rather than MOBILE_BROWSER.
Apache Mobile Filter's Lite Device Detection does the right thing, but I need something I can use from Java.
Can anyone recommend a better solution?
I'm the founder and maintainer of the MobileESP project, a free open source cross-platform library for detecting mobile devices. It's still very much alive! :-)
www.mobileesp.org
MobileESP only gives binary "is mobile" responses. You can detect by platform like iOS, Android or Windows Phone, or by device category, like "iPhone Tier" smartphones vs. tablet. Be sure to take a quick review of the API page.
As you may know, useragent strings vary widely. If the browser shipped on the device, the manufacturer may customize it. For example, HTC often customizes the native Android browser's useragent string.
Google provides recommendations on how the OEM should customize the useragent. If the device should be considered a phone, then Google recommends including the word "mobile" element in the string. But if the device should be considered a tablet, then the string should not contain "mobile." Adherence to this recommendation varies widely, of course.
Third party browsers like Opera or Maxthon can put whatever they want to in the useragent string -- and do! Certain "new" browsers which shall remain nameless have been doing very poor jobs of putting the correct information in their useragent strings for each platform (e.g., Android vs. iOS versions). There's not much you can do unless you get a lot of traffic from these browsers and wish to invest in tracking their exact useragent values per platform and software rev.
Anyway, MobileESP was created with the vision of doing the detection on a page-by-page basis when the page is served. I purposefully wrote the code to be very easy to read and customize, too.
To do the batch processing, you might do something like this:
1.) In the constructor, comment out the initDeviceScan() method. You won't need this for bulk processing.
2.) Pass the UserAgent and an empty string in to the constructor (UAgentInfo()).
3.) Then run whatever detect methods you're interested in. Be thoughtful about the order in which you do them to save time, based on a scan of your users.
For example, if most of your users are on iPhone and that's one of the detection criteria you're interested in, then run that check first. If this example, you certainly wouldn't run the BlackBerry method first!
My contact info is in the source code and on the web site. Send me a note if you have any questions or run into any bugs. Definitely look around the MobileESP.org web site for some tips.
Best wishes on your project, Aniket!
Anthony
Another thread suggests using the following library:
https://github.com/ahand/mobileesp/blob/master/Java/UAgentInfo.java
which seems OK.
How to read the Apache Mobile Filter value in JSP (for Tomcat)?
Before in the httpd.conf file where you have to configure mod_jk you muse add this:
JkEnvVar AMF_IS_MOBILE undefined
The Java code is:
request.getAttribute("AMF_IS_MOBILE")
from: http://wiki.apachemobilefilter.org
51Degrees has a free open source Java API that allows you to run offline processing. You can access it from the GitHub Repository here. https://github.com/51Degrees/Java-Device-Detection.
As part of the API there is an offline processing example (code also shown below) this takes a CSV file of User-Agents and returns the required properties into an Output file. The following example just uses 3 of the properties within the data set, for a full list you can look at the dictionary here https://51degrees.com/resources/property-dictionary
// output file in current working directory
public String outputFilePath = "batch-processing-example-results.csv";
// pattern detection matching provider
private final Provider provider;
/**
* Initialises the device detection Provider with the included Lite data
* file. For more data see:
* <a href="https://51degrees.com/compare-data-options">compare data options
* </a>
*
* #throws IOException if there was a problem reading from the data file.
*/
public OfflineProcessingExample() throws IOException {
provider = new Provider(StreamFactory.create(
Shared.getLitePatternV32(), false));
}
/**
* Reads a CSV file containing User-Agents and adds the IsMobile,
* PlatformName and PlatformVersion information for the first 20 lines.
* For a full list of properties and the files they are available in please
* see: <a href="https://51degrees.com/resources/property-dictionary">
* Property Dictionary</a>
*
* #param inputFileName the CSV file to read from.
* #param outputFilename where to save the file with extra entries.
* #throws IOException if there was a problem reading from the data file.
*/
public void processCsv(String inputFileName, String outputFilename)
throws IOException {
BufferedReader bufferedReader =
new BufferedReader(new FileReader(inputFileName));
try {
FileWriter fileWriter = new FileWriter(outputFilename);
try {
// it's more efficient over the long haul to create a match
// once and reuse it in multiple matches
Match match = provider.createMatch();
// there are 20k lines in supplied file, we'll just do a couple
// of them!
for (int i = 0; i < 20; i++) {
// read next line
String userAgentString = bufferedReader.readLine();
// ask the provider to match the UA using match we created
provider.match(userAgentString, match);
// get some property values from the match
Values isMobile = match.getValues("IsMobile");
Values platformName = match.getValues("PlatformName");
Values platformVersion = match.getValues("PlatformVersion");
// write result to file
fileWriter.append("\"")
.append(userAgentString)
.append("\", ")
.append(getValueForDisplay(isMobile))
.append(", ")
.append(getValueForDisplay(platformName))
.append(", ")
.append(getValueForDisplay(platformVersion))
.append('\n')
.flush();
}
} finally {
fileWriter.close();
}
} finally {
bufferedReader.close();
}
}
/**
* Match values may be null. A helper method to get something displayable
* #param values a Values to render
* #return a non-null String
*/
protected String getValueForDisplay(Values values) {
return values == null ? "N/A": values.toString();
}
/**
* Closes the {#link fiftyone.mobile.detection.Dataset} by releasing data
* file readers and freeing the data file from locks. This method should
* only be used when the {#code Dataset} is no longer required, i.e. when
* device detection functionality is no longer required, or the data file
* needs to be freed.
*
* #throws IOException if there was a problem accessing the data file.
*/
#Override
public void close() throws IOException {
provider.dataSet.close();
}
/**
* Instantiates this class and starts
* {#link #processCsv(java.lang.String, java.lang.String)} with default
* parameters.
*
* #param args command line arguments.
* #throws IOException if there was a problem accessing the data file.
*/
public static void main(String[] args) throws IOException {
System.out.println("Starting Offline Processing Example");
OfflineProcessingExample offlineProcessingExample =
new OfflineProcessingExample();
try {
offlineProcessingExample.processCsv(Shared.getGoodUserAgentsFile(),
offlineProcessingExample.outputFilePath);
System.out.println("Output written to " +
offlineProcessingExample.outputFilePath);
} finally {
offlineProcessingExample.close();
}
}
Hope this helps.
Disclosure: I work at 51Degrees.
To detect iPhone, Android and other mobile devices in Java user-agent can be used. If you are using Spring you can customize the below code as per your need.
#Override
public ModelAndView redirectToAppstore(HttpServletRequest request) {
String userAgent = request.getHeader("user-agent").toLowerCase();
String iphoneStoreUrl = "IPONE_STORE_URL";
String androidStoreUrl = "ANDROID_STORE_URL";
if (userAgent.contains("iphone"))
return new ModelAndView("redirect:" + iphoneStoreUrl);
else if (userAgent.contains("android"))
return new ModelAndView("redirect:" + androidStoreUrl);
return new ModelAndView("redirect:/");
}
From java code i am able to run the vbscript by using this code
Runtime.getRuntime().exec("wscript C:\\ppt\\test1.vbs ");
But want to know how to call the method of vbscript from java..for example in test1.vbs
Set objPPT = CreateObject("PowerPoint.Application")
objPPT.Visible = True
Set objPresentation = objPPT.Presentations.Open("C:\ppt\Labo.ppt")
Set objSlideShow = objPresentation.SlideShowSettings.Run.View
sub ssn1()
objPPT.Run "C:\ppt\Labo.ppt!.SSN"
End sub
how to call only ssn1() method from java.Otherwise can we run the macro of a power point from java code..kindly help!!
This should make you happy :) Go to the WScript section : http://technet.microsoft.com/library/ee156618.aspx
Here's my idea... in your vbscript file, make your script listen to a command line parameter that would specify which method to call. Then, in Java, you could only have to use this parameter whenever you want to call a specific method in the file.
Otherwise, if you want to access powerpoint in java, you will need to access its API like you did in vbscript, which is possible if vbscript can do it but the approach / syntax may change.
I'm not so much into the visual basic script side, but if you can expose your visual basic script as a COM object, the you can access the methods of it from java by usage of frameworks such as for example com4j:
http://com4j.java.net/
The PowerPoint application object's .Run method lets you call any public subroutine or function in any open presentation or loaded add-in
This post answers the OP's question:
Otherwise can we run the macro of a power point from java code..kindly help!!
(but does not address the original vbscript question)
There's the JACOB library, which stands for Java COM Bridge, you can find here: http://sourceforge.net/projects/jacob-project/?source=directory
With it you can invoke Excel, Word, Outlook, PowerPoint application object model methods.
I've tried this with Excel but not PowerPoint. (This is just some sample code, one might want to make it more object oriented.)
public class Excel {
private static ActiveXComponent xl = null;
public static Init() {
try {
ComThread.InitSTA();
xl = ActiveXComponent.connectToActiveInstance("Excel.Application.14");
// 14 is Office 2010, if you don't know what version you can do "Excel.Application"
if (xl==null) {
// code to launch Excel if not running:
xl = new ActiveXComponent("Excel.Application");
Dispatch.put(xl, "Visible", Constants.kTrue);
}
}
catch (Exception e) {
ComThread.Release();
}
}
public static String Run(String vbName) {
// Variant v = Dispatch.call(xl, "Run", vbName); // using string name lookup
Variant v = Dispatch.call(xl, 0x103, vbName); // using COM offset
// return Dispatch.get(this, "Name").getString();
return v.getString();
}
public static Variant Run1p(String vbName, Object param) {
// Variant v = Dispatch.call(xl, "Run", vbName, param);
return Dispatch.call(xl, 0x103, vbName, param);
// return Dispatch.get(this, "Name").getString();
}
public static Worksheet GetActiveWorksheet () {
// Dispatch d = xl.getProperty("ActiveSheet").toDispatch();
Dispatch d = Dispatch.get(xl, 0x133).toDispatch ();
return d; // you may want to put a wrapper around this...
}
}
Notes:
For Excel, at least, to get Run to invoke a VBA macro/subroutine several things have to be true:
The Excel workbook containing the macro must be "Active" (i.e. must
be the ActiveWorkbook) otherwise Run will not find the VBA subroutine. (However the workbook does not have to be
screen visible!! This means you can call a VBA Macro that is in an add-in!).
You can then pass the name of the macro using the following syntax as a string literal:
VBAProjectName.VBAModuleName.SubroutineName
For COM object invocations, you can use the name lookup version or the id number version. The id numbers come from the published COM interfaces (which you can find in C++ header files, or possibly have JACOB look them up for you).
If you successfully did the connection to Excel, be sure to call ComThread.Release() when you're done. Put it in some appropriately surrounding finally. If the process of your Java code terminates without calling it, the COM reference count on Excel will be wrong, and the Excel process will never terminate, even after you exit the Excel application. Once that happens, needless to say, Excel starts to behave screwy then (when you try to use it next, it runs but will fail to load any plug-ins/add-ons). If that happens (as it can during debugging esp. if you are bypassing finally's for better debugging) you have to use the task manager to kill the Excel process.