Как я могу справиться с 32-битными / 64-битными несоответствиями при выполнении IPC через SendMessage? - PullRequest
4 голосов
/ 20 декабря 2010

У меня есть фрагмент кода C ++, который считывает текст элемента дерева (как содержится в простом Представлении дерева общих элементов управления ), используя сообщение окна TVM_GETITEM . Представление дерева, которое получает сообщение, находится в другом процессе, поэтому я использую немного общей памяти для структуры, на которую указывает один из аргументов сообщения окна. Я должен сделать эту работу, поскольку удаленный процесс не находится под моим контролем (я пишу приложение, похожее на Spy ++).

В принципе, это работает хорошо, но терпит неудачу, если целевой процесс существенно отличается:

  1. Если код целевого процесса был создан с определением UNICODE, но мой собственный код не был задан, два процесса будут иметь разные представления о структуре строковых членов в структуре TVITEM, Я решил это уже с помощью вызова IsWindowUnicode и затем явной отправки либо TVM_GETITEMA, либо TVM_GETITEMW (при необходимости перекодируя результат).

  2. Если вызывающий процесс был построен в 32-битном режиме, а целевой процесс является 64-битным (или наоборот), структура (и размер) структуры TVITEM структуры отличаются, так как указатели имеют другой размер.

В настоящее время я пытаюсь найти хороший способ решения второй проблемы. Этот конкретный вариант использования (получение текста элемента дерева) является лишь примером, такая же проблема существует для других оконных сообщений, которые отправляет мой код. Сейчас я рассматриваю два подхода:

  1. Создайте мой код дважды, а затем выполните 32-битный или 64-битный код в зависимости от того, что делает целевой процесс. Это требует некоторых изменений в нашей системе сборки и упаковки, а также требует выделения кода, специфичного для архитектуры, в отдельный процесс (сейчас он находится в DLL). Как только это будет сделано, оно должно работать хорошо.
  2. Определите формат изображения целевого процесса во время выполнения, а затем используйте пользовательские структуры вместо структуры TVITEM , в которой явно используются указатели шириной 32 или 64 бита. Это требует написания кода для определения архитектуры удаленного процесса (я надеюсь, что смогу сделать это, вызвав GetModuleFileName в удаленном процессе, а затем проанализировав заголовок PE с помощью Image Help Library ) и жесткое кодирование двух структур (одна с 32-битными указателями, одна с 64-битными). Кроме того, я должен убедиться, что адрес разделяемой памяти находится в 32-битном адресном пространстве (чтобы мой собственный код всегда мог получить к нему доступ, даже если он скомпилирован в 32-битном режиме).

Кто-нибудь еще должен был решить подобную проблему? Есть ли более простые решения?

Ответы [ 3 ]

1 голос
/ 05 января 2011

Я закончил проверкой, является ли удаленный процесс 32-битным или 64-битным во время выполнения, и затем записал правильную структуру в общую память перед отправкой сообщения.

Например, вот как вы можете использовать сообщение TVM_GETITEM, даже если между вызывающим и получателем сообщения есть 32-битная <-> 64-битная комбинация:

/* This template is basically a copy of the TVITEM struct except that
 * all fields which return a pointer have a variable type. This allows
 * creating different types for different pointer sizes.
 */
template <typename AddrType>
struct TVITEM_3264 {
  UINT      mask;
  AddrType  hItem;
  UINT      state;
  UINT      stateMask;
  AddrType  pszText;
  int       cchTextMax;
  int       iImage;
  int       iSelectedImage;
  int       cChildren;
  AddrType  lParam;
};
typedef TVITEM_3264<UINT32> TVITEM32;
typedef TVITEM_3264<UINT64> TVITEM64;

// .... later, I can then use the above template like this:
LPARAM _itemInfo;
DWORD pid;
::GetWindowThreadProcessId( treeViewWindow, &pid );
if ( is64BitProcess( pid ) ) {
    TVITEM64 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT64)m_item;
    itemInfo.pszText = (UINT64)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
} else {
    TVITEM32 itemInfo;
    ZeroMemory( &itemInfo, sizeof( itemInfo ) );

    itemInfo.mask = TVIF_HANDLE | TVIF_TEXT;
    itemInfo.hItem = (UINT32)m_item;
    itemInfo.pszText = (UINT32)(LPTSTR)sharedMem->getSharedMemory( sizeof(itemInfo) );
    itemInfo.cchTextMax = MaxTextLength;
    _itemInfo = (LPARAM)sharedMem->write( &itemInfo, sizeof(itemInfo) );
}

Функция sharedMem->getSharedMemory - это небольшая вспомогательная функция для получения указателя на область разделяемой памяти; необязательный аргумент функции указывает значение смещения. Важно то, что область разделяемой памяти всегда должна находиться в 32-битном адресном пространстве (чтобы к ней мог обращаться даже 32-битный удаленный процесс).

0 голосов
/ 20 декабря 2010

Я не знаком с этим конкретным сообщением, но если предполагается, что сообщение Windows TVM_GETITEM корректно функционирует во всех процессах, то Windows должна заполнить структуру TVITEM в адресном пространстве вызывающего и обработать любые необходимые преобразования для вас, без необходимостивам предоставить общую память.Если это не так, то я сомневаюсь, что проблема, которую вы видите здесь, легко разрешима без каких-либо неудобных искажений.

Бит общей памяти смущает меня;как правило, вы должны явно уведомить оба процесса о сегменте разделяемой памяти, и вы не упомянули внедрение DLL или что-то в этом роде.Как точно вызываемый абонент узнает о разделе общей памяти в его адресном пространстве и как вы его используете?Вы уверены, что он нужен для этого API?

0 голосов
/ 20 декабря 2010

ИМХО есть проблема дизайна. Я не знаю, почему вы так поступаете, может быть, у вас нет полного контроля над всеми частями. Но в базовой перспективе MVC вы просматриваете значения из представления, а не запрашиваете его у модели.

...