C++ Get the HWND for SketchUp

For my C plugin, I want to display a progress bar while it exports. To do that, I need to get the HWND of the main sketchup window. Is there a best way to do that?

The APIs have never had a method / function to get SketchUp’s application window (despite requests.)
So we’ve had to make Windows system calls using various Ruby libraries (currently Fiddle.)

But on the C side you can load a system DLL (User32.DLL) and make direct system calls.

One way might be …

  1. Get the current SketchUp process ID. (Ruby’s Process::pid module method will return it.)
  2. Enumerate the toplevel windows.
  3. Check each Window’s process ID against what you know is SketchUp’s process ID.
  4. When found keep the handle and return falsely from your EnumWindowsProc.

You might also filter by windows containing “SketchUp” in the caption, but users can have more than 1 instance of SketchUp running.


Others have used the handle of what they know is a child window (perhaps they created it as an empty HtmlDialog offscreen with a unique caption,) used FindWindow using the unique name, and then GetAncestorWindow function looking for the root owner.

2 Likes

Here is my solution. Sorry for the mix of string and wstring.

#include <psapi.h>
#define GWL_HINSTANCE       (-6)
HWND sketchupAppHwnd;
std::wstring activeModelName;

BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam) {
  DWORD dwThreadId, dwProcessId;
  HINSTANCE hInstance;
  wchar_t appBuffer[255];
  wchar_t titleBuffer[255];
  HANDLE hProcess;
  if (!hWnd)
    return TRUE;		// Not a window
  if (!::IsWindowVisible(hWnd))
    return TRUE;		// Not visible
  if (!SendMessage(hWnd, WM_GETTEXT, sizeof(appBuffer), (LPARAM)appBuffer))
    return TRUE;		// No window title
  hInstance = (HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE);
  dwThreadId = GetWindowThreadProcessId(hWnd, &dwProcessId);
  hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
  if (GetModuleFileNameEx(hProcess, hInstance, appBuffer, sizeof(appBuffer)))
  {
    std::wstring procName = appBuffer;
    //procName is the full path to the executable, which could be installed anywhere
    if (hasEndingW(procName, L"\\SketchUp.exe")) //does first string end with second string
    {
      GetWindowText(hWnd, titleBuffer, sizeof(titleBuffer));
      std::wstring titlename = titleBuffer;    
      
      //there could be more that one session of SketchUp running
      //make sure the titlename starts with the name of the active model
      if (hasBeginning(titlename,activeModelName)) //does first string begin with second string
      {
        sketchupAppHwnd = hWnd;
      }
    }
  }

  CloseHandle(hProcess);
  return TRUE;
}

std::string CSketchUpExporter::GetModelName()
{
  SUStringRef name = SU_INVALID;
  SUStringCreate(&name);
  SUModelGetTitle(model_, &name);
  size_t name_length = 0;
  SUStringGetUTF8Length(name, &name_length);
  char* name_utf8 = new char[name_length + 1];
  SUStringGetUTF8(name, name_length + 1, name_utf8, &name_length);
  // Now we have the name in a form we can use'
  std::string  aName(name_utf8);

  SUStringRelease(&name);
  delete[]name_utf8;

  return aName;
}

void MakeCallToGetHandle()
{
  // Get the handle for this session of Sketchup
  if (sketchupAppHwnd == NULL)
  {
    activeModelName = GetModelName();
    EnumWindows(EnumWindowsProc, NULL);
  }
}

If I open SketchUp with the desktop or taskbar icon I get a new unsaved and unmodified model.

Sketchup.active_model.title.inspect
#=> "" # This is filename minus the .skp
Sketchup.active_model.path.inspect
#=> ""

… however the application titlebar reads "Untitled - SketchUp Pro 20xx" when I run SU in English.

If I run in French …the titlebar reads "Sans titre - SketchUp Pro 20xx". So "Untitled" is localized.

Anyway, if the user wants to export an unsaved model and they also have another unsaved model open in another SketchUp instance, there could be confusion.

Just curious, … you are not using the built-ins ?

1 Like

Because we use our own export dialog to save to a server location, we do not use “Save as” method that is made available by using something like

    bool CXmlExporterPlugin::ConvertFromSkp(const std::string& input_skp,
        const std::string& output_xml,
        SketchUpPluginProgressCallback* callback,
        void* reserved)

Is there another way to access SketchUpPluginProgressCallback?

Well, the ConvertFromSkp callback function is responsible for saving the output file wherever that may be. The 2nd string argument is the local path and filename chosen by the user in the SaveAs browser dialog.

Within this function your code can extract the file basename and write it to a server either instead of or as well as the local path.

You also have Ruby available because you are within the SketchUp process. Ruby is itself implemented in C, and so there is a Ruby C API. (The SDK is distro’d with the Ruby headers for this use.)

With this API you can leverage either Ruby’s standard Net::FTP or Net::HTTP classes, … or SketchUp’s Sketchup::Http::Request class.

So you can save it to the local path, and then afterward send it to the sever. (I suggest this as an network error in the middle of saving to a network server could cause issues or the file to be only partial or corrupted. Better to save locally first then copy the file to the server. When done this way you can know what the length of the file is for use gauging the upload progress.)

I do not believe so, … but you can open a request issue in the official API tracker.