Last active
April 24, 2023 07:09
-
-
Save teocomi/fda8ee767d7ec27d1b04614242bcad18 to your computer and use it in GitHub Desktop.
Run Revit commands using Win32 API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added while loops to minimize awaital