Using SendKeys from C# to Sketchup

I have been working on some code to control Sketchup from a C# application. I have not attempted to use the C# wrapper of the Sketchup C++ API because my requirements are fairly limited. I have built a mechanism consisting a SU extension to which a shortcut key has been assigned (I use Ctrl-Shift-L). The extension calls a function exposed by my C# code using a COM interface. The C# code lives inside a Microsoft Word process as a VSTO Add-in. The C# code provides various custom functions to Word, and also exposes a COM interface accessible from external processes.
The C# code first composes some Ruby and loads it into a variable called RubyForSU. The SketchUp extension then does this:

do_this = @word.COMAddIns("MyVstoAddIn").Object.RubyForSU
eval do_this
@word.COMAddIns("MyVstoAddIn").Object.SketchupFinished "OK"

When the SU extension runs the first line of code, it gets back some plain Ruby. The second line of code evaluates the Ruby and executes it. Yes, I know this is a high risk tactic – but in the system I’m building, the end user has no opportunity to stuff arbitrary commands into RubyForSU – it’s all done programmatically. When the Ruby code has completed, SU hits the third line, which calls a C# VSTO method called SketchUpFinished and passes a text status message: “OK” or an error message.

This all works beautifully, with one teeny exception. I don’t want the user to have to activate SU and press Ctrl+Shift+L. I want the C# code to do that by itself as the user is performing various functions via the VSTO addin. Time for SendKeys. And time for an interesting finding.

If you Google the hell out of this, you will see everyone telling you to do something like this (note: my VSTO add-in uses the Windows Forms architecture which you need to use SendKeys. If you’re not writing a WinForms add-in, you would use other native Win32 functions). This example sends keystrokes to Notepad.

using System.Diagnostics;
using System.Windows.Forms;
...
[DllImport("User32.dll")]
public static extern int SetForegroundWindow(IntPtr hWnd);
...

Process p = Process.GetProcessesByName("notepad").FirstOrDefault();
if (p != null)
{
    IntPtr h = p.MainWindowHandle;
    SetForegroundWindow(h);
    SendKeys.SendWait("k");
}

Works like a charm. But not with SketchUp! Well, for a while it did, but then the planets went into a different celestial configuration, and some stray Gamma rays must have hit my machine, because one day it just stopped working. No error. No hangs. Just no keys getting to SketchUp. I further flattened my forehead before I figured it out. Here’s what works:

Process[] processes = Process.GetProcessesByName("Sketchup");
Process p = processes.FirstOrDefault(pr => pr.MainWindowTitle != "");

if (p != null)
{
    IntPtr h = p.MainWindowHandle;    
    SetForegroundWindow(h);
    SendKeys.SendWait("^+l");
}

It turns out that Process.GetProcessesByName(“Sketchup”) returns about 7 or 8 processes, and the first of them has no main window title and returns a handle of 0. When C# does a SendKeys to an Hwnd of 0, the keystrokes go into lala land. The processes.FirstOrDefault(pr => pr.MainWindowTitle != “”) uses a lamda expression to hunt down the process that actually has a windows title (you know, like “My Imaginative House Model - SketchUp Pro 2023”). And that’s the process you need to stuff keystrokes into.

So now I have an obedient servant called SketchUp that I can drive around entirely from within Microsoft Word (or any other C# application). My end user doesn’t need to know anything about SU at all. They just click pretty little buttons, and SketchUp sings and dances. When the C# code wants SU to dance, it preps some Ruby code, then sends a Ctrl+Shift+L to SU, triggering the SU extension that calls back into the C# code to fetch the Ruby, runs it, then reports status.

It’s Miller time.

1 Like

(1) Whatever Ruby code you send should evaluate within a unique namespace module.

(2) You don’t need to send any keychord for a shortcut to fire your interface. This just complicates things as (a) shortcuts are considered end user settings and not settable via the API; and (b) users are stubborn about assigning shortcuts how they wish.

(3) You can run a Ruby script at startup using a command line parameter. In the “startup” script, just call the method that links to your C# object.
See:


Related Info:

SendKeys is also a method of the Windows Scripting Host which is available from SketchUp’s Ruby using the WIN32OLE class.

def send_ctrl_shift_L
  require 'win32ole' unless defined?(WIN32OLE)
  shell = WIN32OLE.new('WScript.Shell')
  shell.SendKeys("^+l")
end

From a command shell running: tasklist /nh | sort
… shows that the application process name is "SketchUp.exe"

All the other little "sketchup_webhelper.exe" are web network support processes.


SketchUp Ruby can get SketchUp’s application process id number simply by calling:

Process.pid

… in case you needed to pass that back to your Word process.