Skip to content

Instantly share code, notes, and snippets.

@imAliAsad
Forked from teocomi/Win32Api.cs
Created March 25, 2018 12:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save imAliAsad/fd944dbd95ad1f156ae915b1e0a29531 to your computer and use it in GitHub Desktop.
Save imAliAsad/fd944dbd95ad1f156ae915b1e0a29531 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);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment