Question

I am trying to create a Visual Studio extension that can record key presses in a text editor and replay them.

I have a IVsTextViewCreationListener that calls AddCommandFilter() to add a command filter to any new text editor being created:

public class VsTextViewCreationListener : IVsTextViewCreationListener
{
    public void VsTextViewCreated(IVsTextView textViewAdapter)
    {
        var filter = new MyCommandFilter();

        IOleCommandTarget next;
        if (ErrorHandler.Succeeded(textViewAdapter.AddCommandFilter(filter, out next)))
            filter.Next = next;
    }
}

The command filter looks like this:

public class MyCommandFilter : IOleCommandTarget
{
    public IOleCommandTarget Next { get; set; }

    public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut)
    {
        if (nCmdID == (uint)VSConstants.VSStd2KCmdID.TYPECHAR)
        {
            // Save values of pguidCmdGroup, nCmdID, nCmdexecopt and GetTypedChar(pvaIn)
            // ...
        }

        return Next.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut);
    }

    public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
    {
        return Next.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText);
    }

    public void Playback()
    {
        // Resend the values
        var pvaIn = Marshal.AllocCoTaskMem(4);
        Marshal.GetNativeVariantForObject((ushort)savedChar, pvaIn);
        Next.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, IntPtr.Zero);
    }

    private static char GetTypedChar(IntPtr pvaIn)
    {
        return (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn);
    }
}

(I have clipped out the portion of the code that holds the values in a list)

It works in the sense that it captures and replays the key presses, however after replaying it often (not always) crashes Visual Studio, and the crash occurs in native code so I don't have much data about the error.

I have never written any VS extension before and surely what I am doing is sketchy at best...

(I should probably release the memory allocated with AllocCoTaskMem() - I have tried it but it was still crashing and I thought at this point it can't harm not releasing it).

Would appreciate any ideas.

Was it helpful?

Solution

Got the answer from the MSDN forums:

The correct size for a VARIANT is 16 bytes, not 4:

Marshal.AllocCoTaskMem(16);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top