Связь между приложениями между JAVA и C # с использованием оконных сообщений - PullRequest
0 голосов
/ 06 августа 2011

Мне нужно общаться с приложением C # с помощью обмена сообщениями Windows из приложения Java. Из моего приложения я регистрируюсь для сообщений, используемых для общения. Я могу успешно получить дескриптор окна приложения C # и зарегистрировать сообщения. Приложение C # отвечает на сообщения, отправляя ответные сообщения WM_COPYDATA. Я могу добраться до точки, где получено WM_COPYDATA. Но я не уверен, как извлечь содержимое сообщения из ответного сообщения.

Действительно помогает, если я могу получить пример кода, который читает содержимое из сообщения WM_COPYDATA из приложения java, используя библиотеки jniwrap и winpack. Будет более полезным, если содержимое lParam имеет тип Structure.

Мне пришлось редактировать код для удаления конфиденциальных данных

Следующий код получает дескриптор окна другого приложения по имени окна, регистрируется для сообщений запроса и ответа, а затем отправляет сообщение запроса с пустым содержимым.

private Library user32; 
private long appHandle; 

public void sendRequest() {
    long requestMsgId = (int)this.registerWindowMessage("WM_TBD_SN_REQEST");
    long responseMsgId = (int)this.registerWindowMessage("WM_TBD_SN_RESPONSE");

    long tbdHandle = findWindow(null, "TestApp");

    this.sendWindowsMessage(new Handle(tbdHandle), new Int(requestMsgId), new Handle(this.appHandle), new Pointer.Void());

}

public long sendWindowsMessage(final Parameter... args) {
    final Function sendMessage = this.user32.getFunction("SendMessageA");
    LongInt longInt = new LongInt();
    sendMessage.invoke(longInt, args);
    return longInt.getValue();
}

public long findWindow(final String classname, final String windowName) {
    final Function findWindow = this.user32.getFunction("FindWindowA");
    Parameter cName = null;
    if (classname == null || classname.equals("")) {
        cName = new Pointer.Void();
    }
    else {
        cName = new AnsiString(classname);
    }
    LongInt longInt = new LongInt();
    findWindow.invoke(longInt, cName, new AnsiString(windowName));
    return longInt.getValue();
}

public long registerWindowMessage(String message) {
    final Function findWindow = this.user32.getFunction("RegisterWindowMessageA");
    LongInt longInt = new LongInt();
    findWindow.invoke(longInt, new AnsiString(message));
    return longInt.getValue();
}

Это пользовательская процедура окна, которая будет заменена родной процедурой для окна моего приложения

public class MyWindowProc extends WindowProc {

    @Override
    public void callback() {

        if (this._msg.getValue() == Msg.WM_COPYDATA) {
//      I can get to this point, but not sure how I can get the information from the message          
//                The WM_TBD_SN_RESPONSE structure consists of four fields
//                1.  hWnd Field --- window handle of the calling application...
//                2.  msg Field --- WM_COPYDATA message code
//                3.  wData Field --- TDB application's window handle
//                4.  pData Field --- contains a CopyDataStruct
//                      CopyDataStruct.pData – contains the Serial Number ----> how to extract this?
//                      CopyDataStruct.dwData – contains the message code for WM_TBD_SN_RESPONSE (this should match responseMsgId)

        }
        else {
            super.callback();
        }

    }
}

Пожалуйста, помогите. Заранее спасибо.

Ответы [ 2 ]

2 голосов
/ 07 августа 2011

Существует гораздо более простой способ отправки сообщений между приложением Java и приложением C #. Используйте сообщения ZeroMQ с использованием транспорта TCP tcp: //127.0.0.1: portnum Руководство ZeroMQ http://zguide.zeromq.org/page:all показывает десятки примеров шаблонов связи, которые вы можете реализовать с помощью всего лишь нескольких строк кода.

Некоторые люди не используют его, потому что он не поддерживает транспорт IPC в Windows, но он поддерживает транспорт TCP, и это прекрасно работает в качестве решения IPC, потому что ядро ​​распознает, что это локальный пункт назначения, и сокращает ненужная обработка стека TCP / IP.

Вы упомянули, что пытаетесь управлять программой на C #, для которой у вас нет доступа к исходному коду. Этот тип вещей часто называют скрепированием экрана, и вам может быть лучше написать простое приложение на C # с использованием Managed Spy или некоторый код C ++ с использованием Spy ++ в качестве посредника для вашего Java-приложения.

2 голосов
/ 07 августа 2011

Прежде всего, я не Java-разработчик, и я не тестировал приведенный ниже код, но я понимаю WM_COPYDATA, поэтому могу дать разумный ответ на ваш вопрос.

Сообщение WM_COPYDATA отправляет указатель на (то есть адрес) COPYDATASTRUCT , который определяется Windows как:

struct COPYDATASTRUCT {
  ULONG_PTR dwData;
  DWORD     cbData;
  PVOID     lpData;
}

Из Java вам придется читать это вручную, используя методы sun.misc.Unsafe class . Под ручным я имею в виду, что вам придется самостоятельно вычислять адреса памяти.

dwData - это целочисленное значение, которое приложение может использовать для себя. lpData - указатель на буфер, содержащий данные, которые приложение хотело передать. cbData - это количество байтов в буфере, которое содержит lpData.

В Windows ULONG_PTR составляет 4 байта в 32-битной системе и 8 байтов в 64-битной системе. DWORD всегда 4 байта. PVOID, который является указателем (то есть адресом памяти), составляет 4 байта в 32-битной системе и 8 байтов в 64-битной системе.

Таким образом, в 32-битной системе dwData со смещением 0, cbData со смещением 4 и lpData со смещением 8. В 64-битной системе dwData по-прежнему со смещением 0, но cbData по смещению 8 и lpData по смещению 16.

import sun.misc;

final int dwDataOffset = 0;
final int cbDataOffset = 4;  // Change to 8 for 64 bit
final int lpDataOffset = 8;  // Change to 16 for 64 bit

int cpDataAddr = this._pData.getValue();         // This will return the address of the struct (I assume this syntax is correct) - change to long for 64 bit
int messageCode= Unsafe.getInt(cpDataAddr+dwDataOffset);  // Change to getLong for 64 bit
int dataSize = Unsafe.getInt(cbDataAddr+cbDataOffset);
int dataAddress = Unsafe.GetInt(cbDataAddr+lpDataOffset); // Change to getLong for 64 bit

// Create a buffer to hold the data from lpData
byte[] data = new byte[dataSize];
for (int i = 0; i < dataSize; i++)
   data[i] = Unsafe.getByte(dataAddress+i);

Как только ваши готовые данные будут содержать необработанные данные, переданные приложением, что в вашем случае является лицензией. Если лицензия является строкой, вы должны быть в состоянии передать байтовый массив конструктору String. Если это более сложная структура данных, вам придется читать ее, используя методы Unsafe, как мы это делали для COPYDATASTRUCT

...