// Retrieves a string when the reference to a string is embedded as a field // within a structure. The lParam to the message is a reference to the struct // // The parameters are: a pointer the struct and its size. // a pointer to the pointer to the string to retrieve. // the max size for the string. // Param "hwnd" the Window Handle // Param "uMsg" the Windows Message // Param "wParam" the Windows wParam // Param "lParam" a pointer to a struct allocated on the stack // Param "cbSize" the size of the structure // Param "pszText" the address of a pointer to a string located with the structure referenced by the lParam // Param "maxLength" the size of the string // Param "remoteBitness" the bitness of the pointer contained within pszText internal static unsafe string GetTextWithinStructureRemoteBitness( IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr lParam, int cbSize, IntPtr pszText, int maxLength, ProcessorTypes remoteBitness, bool ignoreSendResult) { using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(cbSize + (maxLength + 1) * sizeof(char), hProcess)) { // Allocate the space for the object as well as the string // Ensure proper allocation if (rmem.IsInvalid) { return ""; } // Force the string to be zero terminated IntPtr remoteTextArea = new IntPtr((byte*)rmem.Address.ToPointer() + cbSize); if (remoteBitness == ProcessorTypes.Processor32Bit) { // When the structure will be sent to a 32-bit process, // pszText points to an int, not an IntPtr. // remoteTextArea should be a 32-bit address. System.Diagnostics.Debug.Assert(remoteTextArea.ToInt32() == remoteTextArea.ToInt64()); *(int*)((byte*)pszText.ToPointer()) = rmem.Address.ToInt32() + cbSize; } else { *(IntPtr*)((byte*)pszText.ToPointer()) = remoteTextArea; } // Copy the struct to the remote process... rmem.WriteTo(lParam, new IntPtr(cbSize)); // Send the message... IntPtr result = Misc.ProxySendMessage(hwnd, uMsg, wParam, rmem.Address); // Nothing, early exit if (!ignoreSendResult && result == IntPtr.Zero) { return ""; } // Allocate the buffer for the string char[] achRes = new char[maxLength + 1]; // Force the result string not to go past maxLength achRes[maxLength] = '\0'; fixed (char* pchRes = achRes) { // Read the string from the common rmem.ReadFrom(new IntPtr((byte*)rmem.Address.ToPointer() + cbSize), new IntPtr(pchRes), new IntPtr(maxLength * sizeof(char))); // Construct the returned string with an explicit length to avoid // a string with an over-allocated buffer, as would occur // if we simply use new string(achRes). int length = 0; for (; achRes[length] != 0 && length < maxLength; length++) { } return new string(achRes, 0, length); } } } }
// Main method. It simply copies an unmamaged buffer to the remote process, sends the message, and then // copies the remote buffer back to the local unmanaged buffer. internal static int XSendGetIndex(IntPtr hwnd, int uMsg, IntPtr wParam, IntPtr ptrStructure, int cbSize) { using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(cbSize, hProcess)) { if (rmem.IsInvalid) { throw new OutOfMemoryException(); } // Copy the struct to the remote process... rmem.WriteTo(ptrStructure, new IntPtr(cbSize)); // Send the message... int res = Misc.ProxySendMessageInt(hwnd, uMsg, wParam, rmem.Address); // Copy returned struct back to local process... rmem.ReadFrom(ptrStructure, new IntPtr(cbSize)); return res; } } }
// Main method. It simply copies an unmamaged buffer to the remote process, sends the message, and then // copies the remote buffer back to the local unmanaged buffer. internal static bool XSend (IntPtr hwnd, int uMsg, IntPtr ptrStructure1, IntPtr ptrStructure2, int cbSize1, int cbSize2, ErrorValue errorCode) { using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } using (RemoteMemoryBlock rmem1 = new RemoteMemoryBlock(cbSize1, hProcess)) { // Ensure proper allocation if (rmem1.IsInvalid) { return false; } using (RemoteMemoryBlock rmem2 = new RemoteMemoryBlock(cbSize2, hProcess)) { // Ensure proper allocation if (rmem2.IsInvalid) { return false; } // Copy the struct to the remote process... rmem1.WriteTo(ptrStructure1, new IntPtr(cbSize1)); rmem2.WriteTo(ptrStructure2, new IntPtr(cbSize2)); // Send the message... IntPtr res = Misc.ProxySendMessage(hwnd, uMsg, rmem1.Address, rmem2.Address); // check the result if ((errorCode != ErrorValue.NoCheck) && ((errorCode == ErrorValue.Zero && res == IntPtr.Zero) || (errorCode == ErrorValue.NotZero && res != IntPtr.Zero))) { return false; } // Copy returned struct back to local process... rmem1.ReadFrom(ptrStructure1, new IntPtr(cbSize1)); rmem2.ReadFrom(ptrStructure2, new IntPtr(cbSize2)); } } } return true; }
// Helper override to handle common scenario of having a string lParam // Handles bi-directional string marshaling internal static bool XSend (IntPtr hwnd, int uMsg, IntPtr wParam, ref string str, int maxLength) { using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } using (RemoteMemoryBlock rmem = new RemoteMemoryBlock(maxLength * sizeof(char), hProcess)) { if (rmem.IsInvalid) { return false; } // Send the message... if (Misc.ProxySendMessage(hwnd, uMsg, wParam, rmem.Address) == IntPtr.Zero) { return false; } // Read the string from the remote buffer return rmem.ReadString(out str, maxLength); } } }
// A generic GetItemText that sends a message to a control then reads memory out of the controls process. internal static string GetItemText(IntPtr hwnd, int msg, int index, int textLen) { if (textLen <= 0) { return ""; } using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } using (RemoteMemoryBlock rmem = new RemoteMemoryBlock((textLen + 1) * sizeof(char), hProcess)) { // Allocate memory for UNICODE string. if (rmem.IsInvalid) { return ""; } if (Misc.ProxySendMessage(hwnd, msg, new IntPtr(index), rmem.Address) != IntPtr.Zero) { string itemText; // Read the string from the remote buffer if (rmem.ReadString(out itemText, (textLen + 1))) { return itemText; } } } } return ""; //ProcessorTypes localBitness; //ProcessorTypes remoteBitness; //GetProcessTypes(hwnd, out localBitness, out remoteBitness); //if (localBitness == remoteBitness) //{ // return ""; //} //else if (remoteBitness == ProcessorTypes.Processor32Bit) //{ //ToDo: Convert from 64-bit to 32-bit // return ""; //} //else if (remoteBitness == ProcessorTypes.Processor64Bit) //{ //ToDo: Convert from 32-bit to 64-bit // return ""; //} //return ""; }
//------------------------------------------------------ // // Generic Control Methods that support cross process / cross bitness // //------------------------------------------------------ #region Generic Control Methods // A generic GetItemRect that sends a message to a control then reads memory out of the controls process. internal static Rect GetItemRect(IntPtr hwnd, int msg, int index) { using (SafeProcessHandle hProcess = new SafeProcessHandle(hwnd)) { if (hProcess.IsInvalid) { // assume that the hwnd was bad throw new ElementNotAvailableException(); } NativeMethods.Win32Rect rectW32 = NativeMethods.Win32Rect.Empty; int cMem = Marshal.SizeOf(rectW32.GetType()); using (RemoteMemoryBlock remoteMem = new RemoteMemoryBlock(cMem, hProcess)) { // Check if RemoteMmeoryBlock.Allocate returns null. if (remoteMem.IsInvalid) { return Rect.Empty; } unsafe { IntPtr localRectStart = new IntPtr(&rectW32.left); remoteMem.WriteTo(localRectStart, new IntPtr(cMem)); if (Misc.ProxySendMessageInt(hwnd, msg, new IntPtr(index), remoteMem.Address) != 0) { remoteMem.ReadFrom(localRectStart, new IntPtr(cMem)); Misc.MapWindowPoints(hwnd, IntPtr.Zero, ref rectW32, 2); // Errors in RTL handling are sometimes caused by a failure to clearly define // class responsibility for normalizing the rectangles. If a class provides a // normalized rectangle that is subsequently normalized by a caller, Win32Rect // will convert the boundaries to EmptyRect. This is caused by the test in // Win32Rect.IsEmpty, which returns true if (left >= right || top >= bottom). // For various reasons some controls provide normalized rectangles and some // controls do not, so the calling class needs to selectively normalize based // on known behaviors, version information, or other criteria. // CommonXSendMessage is an intermediary, consumed by multiple classes, // and normalizing here introduces some unwarranted complexity into the // responsibility contract. return rectW32.ToRect(false); } } } return Rect.Empty; } //ProcessorTypes localBitness; //ProcessorTypes remoteBitness; //GetProcessTypes(hwnd, out localBitness, out remoteBitness); //if (localBitness == remoteBitness) //{ // return Rect.Empty; //} //else if (remoteBitness == ProcessorTypes.Processor32Bit) //{ //ToDo: Convert from 64-bit to 32-bit // return Rect.Empty; //} //else if (remoteBitness == ProcessorTypes.Processor64Bit) //{ //ToDo: Convert from 32-bit to 64-bit // return Rect.Empty; //} //return Rect.Empty; }