Как читать текст локально открытого документа MS Word, используя Java или Java Native Access - PullRequest
1 голос
/ 25 сентября 2019

У меня есть проблема с чтением текста локально открытого документа MSWord.Что я понимаю, используя следующий подход, учитывая путь к документу, я могу выполнить любую операцию в документе.

https://github.com/java-native-access/jna/blob/master/contrib/msoffice/src/com/sun/jna/platform/win32/COM/util/office/Wordautomation_KB_313193_Mod.java

Но в моем случае у меня есть дескриптор (WinDef.HWND) для локально открытого объекта слова.И я не могу получить локальный путь от него.Я дал код, который я пробую, и я не могу достичь того, что искал.Пожалуйста, дайте любой указатель, как я могу достичь решения выше.

Обратите внимание, что ниже приведен путь к WINWORD.EXE.А System.out.println("File Path: "+desktop.getFilePath());


import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.DesktopWindow;
import com.sun.jna.platform.FileUtils;
import com.sun.jna.platform.WindowUtils;
import com.sun.jna.platform.WindowUtils.NativeWindowUtils;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.Kernel32Util;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.win32.StdCallLibrary;

import java.util.List;

public class NativeWordpadExtractor {
    public static void main(String ar[]){
        executeNativeCommands();
    }
    public static void executeNativeCommands(){
        NativeExtractor.User32 user32 = NativeExtractor.User32.INSTANCE;
        user32.EnumWindows(new WinUser.WNDENUMPROC() {
            int count = 0;
            @Override
            public boolean callback(WinDef.HWND hWnd, Pointer arg1) {
                byte[] windowText = new byte[512];
                user32.GetWindowTextA(hWnd, windowText, 512);
                String wText = Native.toString(windowText);

                // get rid of this if block if you want all windows regardless of whether
                // or not they have text
                if (wText.isEmpty()) {
                    return true;
                }
                if("SampleTextForScreenScrapping_Word - WordPad".equals(wText)){
                    System.out.println("Got the 'Wordpad'" + hWnd + ", class " + hWnd.getClass() +"getPointer"+ hWnd.getPointer()+ " Text: " + wText);
                    //WinDef.HWND notePadHwnd = user32.FindWindowA("Wordpad",null  );
                    byte[] fileText = new byte[1024];

                    System.out.println("fileText : " + WindowUtils.getWindowTitle(hWnd));
                    List<DesktopWindow> desktops=WindowUtils.getAllWindows(true);
                    // Approach 1) For getting a handle to the Desktop object . I am not able to achieve result with this.
                    for(DesktopWindow desktop:desktops){
                        System.out.println("File Path: "+desktop.getFilePath());
                        System.out.println("Title : "+desktop.getTitle());
                    }
                    System.out.println("fileText : " + WindowUtils.getAllWindows(true));
                    // Approach 2) For getting a handle to the native object .
                    // This is also not working 
                    WinDef.HWND editHwnd = user32.FindWindowExA(hWnd, null, null, null);
                    byte[] lParamStr = new byte[512];
                    WinDef.LRESULT resultBool = user32.SendMessageA(editHwnd, NativeExtractor.User32.WM_GETTEXT, 512, lParamStr);
                    System.out.println("The content of the file is : " + Native.toString(lParamStr));
                    return false;
                }
                System.out.println("Found window with text " + hWnd + ", total " + ++count + " Text: " + wText);
                return true;
            }
        }, null);

    }
    interface User32 extends StdCallLibrary {
        NativeExtractor.User32 INSTANCE = (NativeExtractor.User32) Native.loadLibrary("user32", NativeExtractor.User32.class);
        int WM_SETTEXT = 0x000c;
        int WM_GETTEXT = 0x000D;
        int GetWindowTextA(WinDef.HWND hWnd, byte[] lpString, int nMaxCount);
        boolean EnumWindows(WinUser.WNDENUMPROC lpEnumFunc, Pointer arg);
        WinDef.HWND FindWindowA(String lpClassName, String lpWindowName);
        WinDef.HWND FindWindowExA(WinDef.HWND hwndParent, WinDef.HWND hwndChildAfter, String lpClassName, String lpWindowName);
        WinDef.LRESULT SendMessageA(WinDef.HWND paramHWND, int paramInt, WinDef.WPARAM paramWPARAM, WinDef.LPARAM paramLPARAM);
        WinDef.LRESULT SendMessageA(WinDef.HWND editHwnd, int wmGettext, long l, byte[] lParamStr);
        int GetClassNameA(WinDef.HWND hWnd, byte[] lpString, int maxCount);
    }
}


1 Ответ

0 голосов
/ 25 сентября 2019

Я не совсем уверен, что вы можете достичь того, чего хотите, но я сделаю все возможное, чтобы ответить на ваши вопросы, чтобы приблизить вас к цели.

Есть два способа получить файлинформация: одна более общая для Java / JNA, а другая требует, чтобы вы смотрели внутри пространства памяти процесса.Я рассмотрю первый.

Вместо того, чтобы иметь дело с дескриптором окна, давайте получим ID процесса, который будет проще использовать позже.Это относительно просто:

IntByReference pidPtr = new IntByReference();
com.sun.jna.platform.win32.User32.INSTANCE.GetWindowThreadProcessId(hWnd, pidPtr);
int pid = pidPtr.getValue();

(Следует заметить, что у вас, вероятно, должен быть собственный интерфейс User32, расширяющий приведенный выше, чтобы вы могли просто использовать один класс и не нужно полностью квалифицировать версию JNA, как ясделал.)

Теперь, вооружившись PID, есть несколько вариантов, чтобы попытаться получить путь.

  1. Если вам повезло, и пользователь открылнапрямую (вместо «Файл»> «Открыть»), вы можете восстановить используемую ими командную строку, и он, скорее всего, будет иметь путь.Вы можете получить это из класса WMI Win32_Process.Полный код вы можете найти в моем проекте OSHI в классе WindowsOperatingSystem или вы можете попробовать использовать Runtime.getRuntime().exec() для использования версии WMI командной строки: wmic path Win32_Process where ProcessID=1234 get CommandLine, получая результат в BufferedReader (или см. OSHI'sExecutingCommand класс для реализации.)

  2. Если проверка командной строки не удалась, вы можете найти, какие файловые дескрипторы открыты этим процессом.Самый простой способ сделать это - загрузить утилиту Handle (но все ваши пользователи должны будут это сделать), а затем просто выполнить командную строку handle -p 1234.В этом списке будут перечислены открытые файлы, хранящиеся в этом процессе.

  3. Если вы не можете полагаться на то, что ваши пользователи загружают Handle, вы можете попробовать реализовать тот же код самостоятельно.Это недокументированный API, использующий NtQuerySystemInformation.См. Проект JNA Issue 657 для примера кода, который будет повторять все дескрипторы операционной системы, позволяя вам просматривать файлы.Учитывая, что вы уже знаете PID, вы можете сократить итерацию после SYSTEM_HANDLE sh = info.Handles[i];, пропустив оставшуюся часть кода, если sh.ProcessID не соответствует вашему pid.Как указывалось в этом выпуске, указанный код является в основном неподдерживаемым и опасным .Нет гарантии, что он будет работать в будущих версиях Windows.

Наконец, вы можете увидеть, что вы можете сделать с памятью процесса.Вооружившись PID, вы можете открыть Process, чтобы получить дескриптор:

HANDLE pHandle = Kernel32.INSTANCE.OpenProcess(WinNT.PROCESS_QUERY_INFORMATION, false, pid);

Затем вы можете перечислить его модули с помощью EnumProcessModules ;для каждого модуля используйте GetModuleInformation , чтобы получить структуру MODULEINFO .Это дает вам указатель на память, которую вы можете исследовать к своему сердцу.Конечно, для точного знания того, с какими смещениями для поиска какой информации требуется API-интерфейс для исследуемого исполняемого файла (Word, WordPad и т. Д., И соответствующей версии). Вам также необходимы права администратора.Это исследование оставлено читателю в качестве упражнения.

...