Skip to content

Instantly share code, notes, and snippets.

@teocomi
Last active April 24, 2023 07:09
Show Gist options
  • Save teocomi/fda8ee767d7ec27d1b04614242bcad18 to your computer and use it in GitHub Desktop.
Save teocomi/fda8ee767d7ec27d1b04614242bcad18 to your computer and use it in GitHub Desktop.
Run Revit commands using Win32 API
/// <summary>
/// Run Revit commands using Win32 API
/// </summary>
public class Win32Api
{
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern bool SetFocus(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = false)]
public static extern IntPtr GetDlgItem(IntPtr hDlg, int nIDDlgItem);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SendMessage(IntPtr hwnd, uint Msg, IntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern IntPtr SendMessage(HandleRef hWnd, uint Msg, IntPtr wParam, string lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("USER32.DLL")]
public static extern bool PostMessage(IntPtr hWnd, uint msg, uint wParam, uint lParam);
public const Int32 BM_CLICK = 0x00F5;
public const Int32 WM_SETTEXT = 0x000C;
public const Int32 VK_SPACE = 0x20;
public const Int32 WM_KEYDOWN = 0x100;
internal static async Task<string> ExportWarinings(UIApplication uiapp)
{
//paths stuff
var tempDir = Path.Combine(Path.GetTempPath(), "RevitWarnings");
var path = Path.Combine(tempDir, "warning_" + Guid.NewGuid()+".html");
if (!Directory.Exists(tempDir))
Directory.CreateDirectory(tempDir);
//get revit command to open the warnings window
var id = RevitCommandId.LookupPostableCommandId(PostableCommand.ReviewWarnings);
IntPtr mainWin = GetForegroundWindow();
uiapp.PostCommand(id);
//wait until the new window gets in the foreground
while (GetForegroundWindow()== mainWin)
await DelayWork(100);
IntPtr warningsWindow = GetForegroundWindow();
//must be before the thread-blocking export dialog, and must be without 'await'
Task.Run(() => TypeSaveFileName(path));
//click the export button, will show a modal window to Save As
ClickControl(warningsWindow, "&Export...");
//after the save dialog is closed, close the warnings window
ClickControl(warningsWindow, "Close");
return path;
}
/// <summary>
/// The Save As is shown as modal dialog and it blocks the normal execution of the code
/// This function is executed on a new thread with a small delay so that it is not blocked and it can
/// set the file path an then click Save.
/// </summary>
/// <param name="text"></param>
private static async void TypeSaveFileName(string text)
{
//wait until the new window gets in the foreground
IntPtr mainWin = GetForegroundWindow();
while (GetForegroundWindow() == mainWin)
await DelayWork(100);
IntPtr exportWindow = GetForegroundWindow();
IntPtr comboHandle = GetDlgItem(exportWindow, 13006);
IntPtr editHandle = GetDlgItem(comboHandle, 1001);
SetControlText(comboHandle, 1001, text);
SetFocus(editHandle);
//need to simulate a keystroke or the path won't come throught
PostMessage(editHandle, WM_KEYDOWN, VK_SPACE, 0);
await DelayWork(100);
ClickControl(exportWindow, "&Save");
}
/// <summary>
/// Clicks a button using Win32API
/// </summary>
/// <param name="windowHandle">Handle of the windows the control belongs to</param>
/// <param name="buttonText">Label of the button, to get it correctly use WinSpy</param>
private static void ClickControl(IntPtr windowHandle, string buttonText)
{
IntPtr export = FindWindowEx(windowHandle, IntPtr.Zero, null, buttonText);
SendMessage(export, BM_CLICK, IntPtr.Zero, IntPtr.Zero);
}
/// <summary>
/// Sets the text value of a control using Win32API
/// </summary>
/// <param name="windowHandle">Handle of the windows the control belongs to</param>
/// <param name="controlId">ID of the control, to get it use WinSpy and convert the value fron Hex to Decimal</param>
/// <param name="text">String to be set</param>
private static void SetControlText(IntPtr windowHandle, int controlId, string text)
{
IntPtr iptrHWndControl = GetDlgItem(windowHandle, controlId);
HandleRef hrefHWndTarget = new HandleRef(null, iptrHWndControl);
SendMessage(hrefHWndTarget, WM_SETTEXT, IntPtr.Zero, text);
}
/// <summary>
/// Simple function to delay the code async, so that the windows can be loaded
/// </summary>
/// <param name="i"></param>
/// <returns></returns>
private static async Task DelayWork(int i)
{
await Task.Delay(i);
}
}
@teocomi
Copy link
Author

teocomi commented Feb 22, 2017

Added while loops to minimize awaital

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment