Reputation: 8962
I am writing an OSX Quick-look alternative for Windows using Java and was having trouble on how to get the active selections of file in a n active Explorer window, below is my attempt:
@Override
public void nativeKeyReleased(NativeKeyEvent e) {
System.out.println("key up:"
+ NativeKeyEvent.getKeyText(e.getKeyCode()));
if (e.getKeyCode() == NativeKeyEvent.VK_SPACE) {
System.out.println("Space detected! intercept active window");
char[] buffer = new char[MSWindowConstants.MAX_TITLE_LENGTH * 2];
User32DLL.GetWindowTextW(User32DLL.GetForegroundWindow(),
buffer, MSWindowConstants.MAX_TITLE_LENGTH);
System.out.println("Active window title: "
+ Native.toString(buffer));
PointerByReference pointer = new PointerByReference();
User32DLL.GetWindowThreadProcessId(
User32DLL.GetForegroundWindow(), pointer);
Pointer process = Kernel32.OpenProcess(
Kernel32.PROCESS_QUERY_INFORMATION
| Kernel32.PROCESS_VM_READ, false,
pointer.getValue());
Psapi.GetModuleBaseNameW(process, null, buffer,
MSWindowConstants.MAX_TITLE_LENGTH);
System.out.println("Active window process: "
+ Native.toString(buffer));
if(MSWindowConstants.SHELL_PROCESS_NAME.equals(Native.toString(buffer))){
System.out.println("shell focused! intercept selection");
// retrieve selected FileItems and get the path ...
//Ole32.INSTANCE
}
}
The MSEnumeration class:
public class MSEnumeration {
public static class Psapi {
static {
Native.register("psapi");
}
public static native int GetModuleBaseNameW(Pointer hProcess,
Pointer hmodule, char[] lpBaseName, int size);
}
public static class Kernel32 {
static {
Native.register("kernel32");
}
public static int PROCESS_QUERY_INFORMATION = 0x0400;
public static int PROCESS_VM_READ = 0x0010;
public static native int GetLastError();
public static native Pointer OpenProcess(int dwDesiredAccess,
boolean bInheritHandle, Pointer pointer);
}
public static class User32DLL {
static {
Native.register("user32");
}
public static native int GetWindowThreadProcessId(HWND hWnd,
PointerByReference pref);
public static native HWND GetForegroundWindow();
public static native int GetWindowTextW(HWND hWnd, char[] lpString,
int nMaxCount);
}
// public static class Shell32DLL{
// static {
// Native.register("shell32");
// }
//
// public static native Shell32 Windows();
// }
//
// public static class SHDocVwDLL{
// static {
// Native.register("shdocvw");
// }
//
// public static native ShellWindows ShellWindows();
//
// }
}
I was confused by how to implement the following in JNA:
Get current selection in WindowsExplorer from a C# application?
IntPtr handle = GetForegroundWindow();
List<string> selected = new List<string>();
var shell = new Shell32.Shell();
foreach(SHDocVw.InternetExplorer window in shell.Windows())
{
if (window.HWND == (int)handle)
{
Shell32.FolderItems items = ((Shell32.IShellFolderViewDual2)window.Document).SelectedItems();
foreach(Shell32.FolderItem item in items)
{
selected.Add(item.Path);
}
}
}
How can I translate this into JNA calls?
I looked into JNA'S Shell32 class and COM(Ole32 classes) but that still didn't get me anywhere.
The only workaround I can think of now is to compile the given C# into a separate executable that takes arguments and return the paths of the files, but I don't really like the idea of embedding another executable in java.
EDIT:
Some progress:
public static final String CLSID_ShellWindows = "9BA05972-F6A8-11CF-A442-00A0C90A8F39";
public static final String IID_IShellWindows = "85CB6900-4D95-11CF-960C-0080C7F4EE85";
HRESULT hr = Ole32.INSTANCE
.CoCreateInstance(
GUID.fromString(CLSID_ShellWindows),
null,
WTypes.CLSCTX_ALL,
GUID.fromString(IID_IShellWindows),
p);
System.out.println("result:" + W32Errors.SUCCEEDED(hr)
+ "raw:" + hr.toString());
but the result is never true for some reason...
Upvotes: 1
Views: 1124
Reputation: 8962
I gave up with JNA(well, for this specific task) and used com4j (with excellent documentations) instead.
You would first generate codes for the dll you want, in this case it's shell32.dll
, using tlbimp.jar
from com4j
This example might be a bit outdated but I'll put it here anyway
if (isExplorer(getHWNDProcessName(hwnd))) {
IWebBrowser2 browser = getIWebBrowser2(hwnd);
IShellFolderViewDual3 view = getIShellFolderViewDual3(browser);
if (view != null && browser.visible()) {
lastHWND = hwnd;
FolderItems items = view.selectedItems();
ArrayList<Path> paths = new ArrayList<>(items.count());
for (Com4jObject object : items) {
FolderItem item = object.queryInterface(FolderItem.class);
if (item != null) {
paths.add(Paths.get(item.path()));
// this is for example only, do not create a new File just to get length
System.out.println("file: " + item.path() + " length: "
+ new File(item.path()).length() + " type:" + item.type());
}
}
}
}
// some methods used in the example...
public static IWebBrowser2 getIWebBrowser2(HWND hWnd) {
// TODO this can be potentially optimized
IShellWindows windows = ClassFactory.createShell().windows()
.queryInterface(IShellWindows.class);
for (Com4jObject window : windows) {
IWebBrowser2 browser = window.queryInterface(IWebBrowser2.class);
if (browser.hwnd() == getHWNDValue(hWnd))
return browser;
}
return null;
}
public static IShellFolderViewDual3 getIShellFolderViewDual3(IWebBrowser2 browser) {
if (browser == null)
return null;
return browser.document().queryInterface(IShellFolderViewDual3.class);
}
Note: some method calls might be missing, I only posted essential parts on how to get the selected items.
IMPORTANT
You will need both Shell32.dll
and Shdocvw.dll
for this, so what you want to do is generate codes twice with different dlls
java -jar tlbimp.jar -o wsh -p test.wsh %WINDIR%\system32\Shell32.dll
java -jar tlbimp.jar -o wsh -p test.wsh %WINDIR%\system32\Shdocvw.dll
So that we can have IWebBrowser2
and other nice stuff to work with, for a list of what you can do with this class, please refer to the docs
Upvotes: 2