How can I deal with 32-bit / 64-bit inconsistencies when doing IPC through SendMessage?

I have a piece of C ++ code that reads the text of a tree element (contained in the usual general form of the control tree ) using the message TVM_GETITEM . The tree view that mesage receives is in a different process, so I use a bit of shared memory for the structure that one of the arguments to the window message points to. I have to do this work as the remote process is not under my control (I am writing an application similar to Spy ++).

This works well in principle, but fails if the target process is significantly different:

  • If the code of the target process was created with UNICODE installed, but my own code was not, the two processes will have different ideas about the structure of the line members in the TVITEM structure . I solved this already by calling IsWindowUnicode , and then explicitly sent either TVM_GETITEMA or TVM_GETITEMW (if necessary, recoding the result).

  • If the calling process was built in 32-bit mode and the target process was 64-bit (or vice versa), the layout (and size) of the TVITEM structure is different from pointers of different sizes.

I am currently trying to find a good way to solve the second problem. This specific use case (getting the text of an element tree) is just an example, the same problem exists for other window messages that my code sends. I am now considering two approaches:

  • Create my code twice, and then execute either 32-bit or 64-bit code, depending on what the target process is doing. This requires some changes to our build and packaging system, and this requires factoring the code, which is architecture-specific, into a dedicated process (right now in the DLL). Once this is done, it should work well.
  • Define the image format of the target process at run time, and then use custom structures instead of the structure of the TVITEM structure , which explicitly uses 32-bit or 64-bit width pointers. This requires writing code to discover the architecture of the remote process (I hope I can do this by calling GetModuleFileName on the remote process and then analyzing the PE header using the Image Help Library ) and hardcoding two structures (one with 32-bit pointers, one with 64-bit). Also, I have to make sure that the shared memory address is in the 32-bit address space (so that my own code can always access it, even if it is compiled in 32-bit mode).

Does anyone else have to solve a similar problem? Are there any simpler solutions?

+4
source share
3 answers

I ended up checking if the remote process is 32-bit or 64-bit at runtime, and then writing the correct structure to shared memory before sending the message.

For example, here you can use the message TVM_GETITEM , even if there is a 32-bit and a 64-bit mix between the caller and the receiver:

 /* 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) ); } 

The sharedMem->getSharedMemory is a small helper function for getting a pointer to a shared memory region; an optional function argument indicates the offset value. The important thing is that the shared memory area should always be in a 32-bit address space (so that even a 32-bit remote process can access it).

+1
source

IMHO there is a design problem. I don’t know why you are doing this, maybe you do not have full control over all parts. But in the main MVC perspective, you look at the values ​​from the view, rather than defining its model.

0
source

I am not familiar with this specific message, but if the Windows TVM_GETITEM message should function normally in different processes, then Windows should fill in the TVITEM structure in the address space of the caller and process the conversions you need without the need to provide shared memory. If this is not the case, then I doubt that the problem you see here is easily solvable without any uncomfortable distortion.

The shared memory bit confuses me; as a rule, you should make both processes explicitly aware of the shared memory segment, and you have not mentioned the injection of the DLL or something like that. How exactly is a shared memory partition reported in its address space and how do you use it? Are you sure this is necessary for this API?

0
source

Source: https://habr.com/ru/post/1332732/


All Articles