关于修改JavaFX程序以自动交互Delphi7程序的技术方案问询
修改JavaFX程序以自动交互Delphi 7程序的两种技术方案
嘿,这个需求我之前帮朋友处理过类似的,给你两种靠谱的方向,你可以根据自己的技术栈和复杂度来选:
方案一:自动触发Delphi对话框并填充值(模拟用户交互)
这个方案的核心是用Java调用Windows原生API,模拟用户手动打开对话框、输入值、点击确认的操作,优点是对Delphi程序完全无侵入,不用改它的代码。
具体步骤:
- 给JavaFX项目引入**JNA(Java Native Access)**库,它能让Java直接调用Windows API,不用写复杂的JNI代码。
- 用Windows自带的**Spy++**工具,找到Delphi程序主窗口的标题、对话框输入控件的类名(Delphi的输入框通常是
TEdit)、确认按钮的类名(TButton)和按钮文本,这些是定位控件的关键。 - 用JNA调用Windows API的
FindWindow、FindWindowEx找到对应窗口和控件的句柄,再用SendMessage给控件发送消息填充值、模拟点击。
简化的Java代码示例:
// 先通过JNA映射需要的Windows API public interface User32 extends StdCallLibrary { User32 INSTANCE = Native.load("user32", User32.class); HWND FindWindow(String lpClassName, String lpWindowName); HWND FindWindowEx(HWND hwndParent, HWND hwndChildAfter, String lpszClass, String lpszWindow); int SendMessage(HWND hWnd, int msg, WPARAM wParam, String lParam); // 定义Windows消息常量 int WM_SETTEXT = 0x000C; int BM_CLICK = 0x00F5; } // 在JavaFX的业务方法里调用 public void autoFillDelphiDialog(String windowTitle, String inputValue) { // 定位Delphi程序的主窗口 HWND mainWindow = User32.INSTANCE.FindWindow(null, windowTitle); if (mainWindow == null) { System.out.println("找不到目标Delphi程序窗口"); return; } // 找到对话框里的输入控件(替换成你用Spy++查到的类名/文本) HWND editControl = User32.INSTANCE.FindWindowEx(mainWindow, null, "TEdit", null); if (editControl != null) { // 给输入框填充值 User32.INSTANCE.SendMessage(editControl, User32.WM_SETTEXT, null, inputValue); } // 找到确认按钮并模拟点击 HWND confirmBtn = User32.INSTANCE.FindWindowEx(mainWindow, null, "TButton", "确定"); if (confirmBtn != null) { User32.INSTANCE.SendMessage(confirmBtn, User32.BM_CLICK, null, null); } }
- 小提示:如果Delphi的对话框是模态的,你需要先触发它弹出(比如给主窗口发送消息触发显示对话框的事件),再定位控件;另外如果有多个同类型控件,可能需要通过控件的相对位置或者更精确的文本定位。
方案二:直接修改Delphi程序的内存值(绕过界面)
如果觉得模拟交互太繁琐,或者需要更高的效率,可以直接找到Delphi程序中存储该值的内存地址,用Java修改内存,缺点是复杂度高,需要一点逆向知识。
具体步骤:
- 用Cheat Engine这类内存调试工具,定位到Delphi程序中存储目标值的内存地址。如果是全局变量,能找到静态基址+偏移量(重启程序后地址不变);如果是堆内存,每次重启地址会变,需要通过特征码扫描或者指针链定位。
- 用JNA调用Windows API的
OpenProcess打开Delphi程序的进程,申请写内存的权限。 - 调用
WriteProcessMemory把JavaFX生成的新值直接写入找到的内存地址。
简化的Java代码示例:
public interface Kernel32 extends StdCallLibrary { Kernel32 INSTANCE = Native.load("kernel32", Kernel32.class); HANDLE OpenProcess(int dwDesiredAccess, boolean bInheritHandle, int dwProcessId); boolean WriteProcessMemory(HANDLE hProcess, Pointer lpBaseAddress, byte[] lpBuffer, int nSize, IntByReference lpNumberOfBytesWritten); boolean CloseHandle(HANDLE hObject); } // 写入内存的方法,processId是Delphi程序的进程ID,memoryAddress是找到的内存地址 public void writeToDelphiMemory(int processId, long memoryAddress, int valueToWrite) { // 打开进程,申请写内存和操作内存的权限 int processAccess = 0x0020 | 0x0008; // PROCESS_VM_WRITE | PROCESS_VM_OPERATION HANDLE processHandle = Kernel32.INSTANCE.OpenProcess(processAccess, false, processId); if (processHandle == null) { System.out.println("无法打开Delphi进程,可能权限不足"); return; } // 把要写入的int值转成字节数组 byte[] valueBytes = ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(valueToWrite).array(); Pointer memoryPointer = new Pointer(memoryAddress); IntByReference bytesWritten = new IntByReference(0); // 执行内存写入 boolean success = Kernel32.INSTANCE.WriteProcessMemory(processHandle, memoryPointer, valueBytes, valueBytes.length, bytesWritten); if (success) { System.out.println("内存修改成功,写入了" + bytesWritten.getValue() + "字节"); } else { System.out.println("内存修改失败"); } // 记得关闭进程句柄,避免资源泄漏 Kernel32.INSTANCE.CloseHandle(processHandle); }
- 小提示:修改内存有风险,可能导致Delphi程序崩溃,建议先在测试环境反复验证;如果程序开启了ASLR(地址空间布局随机化),静态基址可能会变,需要重新定位。
两种方案各有优劣:方案一简单安全,适合大多数场景;方案二高效但需要逆向知识。你可以根据自己的实际情况选择。
内容来源于stack exchange
相关产品推荐
相关产品推荐

