0

We are trying to read the ToolTips from system tray icons and the code is working but is returning zero intermittently for the the method below calling Kernel32.VirtualAllocEx

    IntPtr ipRemoteBuffer = Kernel32.VirtualAllocEx(
    hProcess,
    IntPtr.Zero,
    new UIntPtr(BUFFER_SIZE),
    MemAllocationType.COMMIT,
    MemoryProtection.PAGE_READWRITE);

  if (ipRemoteBuffer == IntPtr.Zero)
    return String.Empty;

It seems to work absolutely fine then suddenly stops working and returns IntPtr.Zero consistently. When checking Marshal.GetLastWin32Error() it returns 8 (not enough memory). Below is the full code:

public static string GetTooltip(string search)
{
  IntPtr _ToolbarWindowHandle = GetSystemTrayHandle();

  UInt32 count = User32.SendMessage(_ToolbarWindowHandle, TB.BUTTONCOUNT, 0, 0);

  List<string> tooltips = new List<string>();

  for (int i = 0; i < count; i++)
  {
    TBBUTTON tbButton = new TBBUTTON();
    string text = String.Empty;
    IntPtr ipWindowHandle = IntPtr.Zero;

    text = GetTBButtonText(_ToolbarWindowHandle, i, ref tbButton, ref text, ref ipWindowHandle);

    if (!String.IsNullOrWhiteSpace(text) && text.ToLowerInvariant().Contains(search.ToLowerInvariant()))
      return text;
  }

  return String.Empty;
}

static unsafe string GetTBButtonText(IntPtr hToolbar, int i, ref TBBUTTON tbButton, ref string text, ref IntPtr ipWindowHandle)
{
  const int BUFFER_SIZE = 0x1000;

  byte[] localBuffer = new byte[BUFFER_SIZE];

  UInt32 processId = 0;
  UInt32 threadId = User32.GetWindowThreadProcessId(hToolbar, out processId);

  IntPtr hProcess = Kernel32.OpenProcess(ProcessRights.ALL_ACCESS, false, processId);
  if (hProcess == IntPtr.Zero)
    return String.Empty;

  IntPtr ipRemoteBuffer = Kernel32.VirtualAllocEx(
    hProcess,
    IntPtr.Zero,
    new UIntPtr(BUFFER_SIZE),
    MemAllocationType.COMMIT,
    MemoryProtection.PAGE_READWRITE);

  if (ipRemoteBuffer == IntPtr.Zero)
  {
    var error = Marshal.GetLastWin32Error();
    return String.Empty;
  }

  // TBButton
  fixed (TBBUTTON* pTBButton = &tbButton)
  {
    IntPtr ipTBButton = new IntPtr(pTBButton);

    int b = (int)User32.SendMessage(hToolbar, TB.GETBUTTON, (IntPtr)i, ipRemoteBuffer);
    if (b == 0)
      return String.Empty;

    // this is fixed
    Int32 dwBytesRead = 0;
    IntPtr ipBytesRead = new IntPtr(&dwBytesRead);

    bool b2 = Kernel32.ReadProcessMemory(
      hProcess,
      ipRemoteBuffer,
      ipTBButton,
      new UIntPtr((uint)sizeof(TBBUTTON)),
      ipBytesRead);

    if (!b2)
      return String.Empty;
  }

  // button text
  fixed (byte* pLocalBuffer = localBuffer)
  {
    IntPtr ipLocalBuffer = new IntPtr(pLocalBuffer);

    int chars = (int)User32.SendMessage(hToolbar, TB.GETBUTTONTEXTW, (IntPtr)tbButton.idCommand, ipRemoteBuffer);
    if (chars == -1) { Debug.Assert(false); return ""; }

    // this is fixed
    Int32 dwBytesRead = 0;
    IntPtr ipBytesRead = new IntPtr(&dwBytesRead);

    bool b4 = Kernel32.ReadProcessMemory(
      hProcess,
      ipRemoteBuffer,
      ipLocalBuffer,
      new UIntPtr(BUFFER_SIZE),
      ipBytesRead);

    if (!b4)
      return String.Empty;

    text = Marshal.PtrToStringUni(ipLocalBuffer, chars);

    return text;
  }
}
user351711
  • 3,171
  • 5
  • 39
  • 74
  • 1
    Call `Marshal.GetLastError` to get the error code when any P/Invoke fails. – Sriram Sakthivel Sep 06 '14 at 09:36
  • It's returning 8 which seems to be ERROR_NOT_ENOUGH_MEMORY 8 (0x8) Not enough storage is available to process this command. I'm not out of memory in anyway so will have to do some research on this. Thanks for the response. – user351711 Sep 06 '14 at 09:44
  • Down voting with no comment? – user351711 Sep 06 '14 at 14:05
  • Yes, that is standard practice. Downvotes do not require a comment. Neither do upvotes. Likewise, you should not leave a comment *in response* to a downvote. – Cody Gray - on strike Sep 08 '14 at 07:58
  • I guess that just didn't make sense to me, either someone knows something about what I am asking in which case they point out where I have gone wrong or how I have totally missed the point/been an idiot or they don't know anything about it in which case they don't comment and move on. Oh well.... hopefully someone with more experience of Interop can still help. Downvote for people being rude and abusive yes but not for not knowing something :S – user351711 Sep 08 '14 at 08:37

1 Answers1

0

OK if I make a call to release the memory like so the problem is solved.

    const uint MEM_RELEASE = 0x8000;

    UIntPtr uintPtr = UIntPtr.Zero;
    var successfullyReleased = Kernel32.VirtualFreeEx(hProcess, ipRemoteBuffer, uintPtr, MEM_RELEASE);
    if (!successfullyReleased)
    {

    }
user351711
  • 3,171
  • 5
  • 39
  • 74