I have a MS Word Application Add-in written with VSTO. It contains a button used to create new Letter documents. When pressed a document is instantiated, a WPF dialog is displayed to capture information and then the information is inserted into the document.
On one of my test machines I get the following exception when approximately 40 letters are created in a single Word session:
The disk is full. Free some space on this drive, or save the document
on another disk.
Try one or more of the following:
Close any unneeded documents, programs or windows.
Save the document to another disk.
So I monitored the Winword.exe process using Task Manager:
Memory starts at 97,000k
Memory steadily increases with each letter document until the error is seen at approximately 1,000,000k
If I then close all the documents the memory only drops down to 500,000k
Any tips on how I can troubleshoot the memory leak?
I've gone through my code and ensured that event handlers are unregistered and that i'm disposing objects that need disposing.
Any reference articles that I should be reading?
-- Edit --
Malick, I use unmanaged code to make the WPF window look like an Office dialog. Is there a better way of doing this? I'll try removing it. (edit, there wasn't a change. I'll try the memory monitoring tools)
public class OfficeDialog : Window
{
[DllImport("user32.dll")]
static extern int GetWindowLong(IntPtr hwnd, int index);
[DllImport("user32.dll")]
static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hwnd, IntPtr hwndInsertAfter, int x, int y, int width, int height, uint flags);
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hwnd, uint msg, IntPtr wParam, IntPtr lParam);
const int GWL_EXSTYLE = -20;
const int WS_EX_DLGMODALFRAME = 0x0001;
const int SWP_NOSIZE = 0x0001;
const int SWP_NOMOVE = 0x0002;
const int SWP_NOZORDER = 0x0004;
const int SWP_FRAMECHANGED = 0x0020;
const uint WM_SETICON = 0x0080;
const int ICON_SMALL = 0;
const int ICON_BIG = 1;
public OfficeDialog()
{
this.ShowInTaskbar = false;
//this.Topmost = true;
}
public new void ShowDialog()
{
try
{
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
catch (System.ComponentModel.Win32Exception ex)
{
Message.LogWarning(ex);
//this.Topmost = true;
var helper = new WindowInteropHelper(this);
using (Process currentProcess = Process.GetCurrentProcess())
helper.Owner = currentProcess.MainWindowHandle;
base.ShowDialog();
}
}
protected override void OnSourceInitialized(EventArgs e)
{
base.OnSourceInitialized(e);
RemoveIcon(this);
HideMinimizeAndMaximizeButtons(this);
//using (Process currentProcess = Process.GetCurrentProcess())
// SetCentering(this, currentProcess.MainWindowHandle);
}
public static void HideMinimizeAndMaximizeButtons(Window window)
{
const int GWL_STYLE = -16;
IntPtr hwnd = new WindowInteropHelper(window).Handle;
long value = GetWindowLong(hwnd, GWL_STYLE);
SetWindowLong(hwnd, GWL_STYLE, (int)(value & -131073 & -65537));
}
public static void RemoveIcon(Window w)
{
// Get this window's handle
IntPtr hwnd = new WindowInteropHelper(w).Handle;
// Change the extended window style to not show a window icon
int extendedStyle = OfficeDialog.GetWindowLong(hwnd, GWL_EXSTYLE);
OfficeDialog.SetWindowLong(hwnd, GWL_EXSTYLE, extendedStyle | WS_EX_DLGMODALFRAME);
// reset the icon, both calls important
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_SMALL, IntPtr.Zero);
OfficeDialog.SendMessage(hwnd, WM_SETICON, (IntPtr)ICON_BIG, IntPtr.Zero);
// Update the window's non-client area to reflect the changes
OfficeDialog.SetWindowPos(hwnd, IntPtr.Zero, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);
}
static void SetCentering(Window win, IntPtr ownerHandle)
{
bool isWindow = IsWindow(ownerHandle);
if (!isWindow) //Don't try and centre the window if the ownerHandle is invalid. To resolve issue with invalid window handle error
{
//Message.LogInfo(string.Format("ownerHandle IsWindow: {0}", isWindow));
return;
}
//Show in center of owner if win form.
if (ownerHandle.ToInt32() != 0)
{
var helper = new WindowInteropHelper(win);
helper.Owner = ownerHandle;
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
else
win.WindowStartupLocation = WindowStartupLocation.CenterOwner;
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool IsWindow(IntPtr hWnd);
}
Related
This question already has answers here:
Opening process and changing window position
(3 answers)
Closed 5 years ago.
The event method below brings up the windows system date time clock window. My label is on the lower right side of my form and the system date time clock window appears on the upper left side of my form. Is there a way to position this date time clock window to be on the lower right side of my form when this event handler is clicked?
private void LabelDateTime_Click(object sender, System.EventArgs e)
{
// bring up the date & time dialog
System.Diagnostics.Process.Start("timedate.cpl");
}
Starting a process using System.Diagnostics.Process.Start() in this manner, is not effective, since the generated Process will exit immediately after the window is created. A .cpl applet is not a standard executable and needs then operating system shell and a launcher to start.
However, a stable process can be created using Rundll32.exe, which will generate some threads to host the applet controls and the GDI+ support.
Reaching the applet Window requires some P/Invoke(ing), though, since rundll is window-less and it doesn't reference the one it helps create, so the Process.MainWindowHandle = 0.
Doc Ref. MSDN EnumThreadWndProc() Callback, EnumThreadWindows(), GetWindowRect(), GetWindowText(), SetWindowPos()
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
ProcessStartInfo psInfo = new ProcessStartInfo() {
UseShellExecute = true,
FileName = "rundll32.exe",
Arguments = "shell32.dll, Control_RunDLL timedate.cpl,,0", //<- 0 = First Tab
WindowStyle = ProcessWindowStyle.Normal
};
Process sysClockProcess = new Process() {
SynchronizingObject = this,
EnableRaisingEvents = true,
StartInfo = psInfo
};
sysClockProcess.Start();
sysClockProcess.WaitForInputIdle();
//Insert the Window title. It's case SENSITIVE
//Window Title in HKEY_CURRENT_USER\Software\Classes\Local Settings\MuiCache\[COD]\[LANG]\
string windowTitle = "Date and Time";
int maxLenght = 256;
SetWindowPosFlags flags = SetWindowPosFlags.NoSize |
SetWindowPosFlags.AsyncWindowPos |
SetWindowPosFlags.ShowWindow;
//The first thread is the Main thread. All Dialog windows' handles are attached here.
//The second thread is for GDI+ Hook Window. Ignore it.
EnumThreadWindows((uint)sysClockProcess.Threads[0].Id, (hWnd, lParam) =>
{
StringBuilder lpString = new StringBuilder(maxLenght);
if (GetWindowText(hWnd, lpString, maxLenght) > 0)
if (lpString.ToString() == windowTitle)
{
GetWindowRect(hWnd, out RECT lpRect);
Size size = new Size(lpRect.Right - lpRect.Left, lpRect.Bottom - lpRect.Top);
//Caculate the position of the Clock Windows relative to the ref. Form Size
SetWindowPos(hWnd, (IntPtr)0, ((this.Width + this.Left) - size.Width),
((this.Height + this.Top) - size.Height), 0, 0, flags);
return false;
}
//Window not found: return true to continue the enumeration
return true;
}, ref windowTitle);
sysClockProcess.Exited += (s, ev) => {
Console.WriteLine($"The process has exited. Code: " +
$"{sysClockProcess.ExitCode} Time: {sysClockProcess.ExitTime}");
sysClockProcess.Dispose();
};
Win32 declarations:
// SetWindowPos() flags
[Flags]
public enum SetWindowPosFlags : uint
{
NoSize = 0x0001,
NoActivate = 0x0010,
ShowWindow = 0x0040,
DeferErase = 0x2000,
AsyncWindowPos = 0x4000
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
//Callback for `EnumThreadWindows()`.
public delegate bool EnumThreadWndProc([In] IntPtr hWnd, [In] IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows([In] uint dwThreadId, [In] EnumThreadWndProc lpfn, [In] ref string lParam);
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, [Out] StringBuilder lpString, [In] int nMaxCount);
[DllImport("user32.dll", SetLastError = true)]
static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
[DllImport("user32.dll", SetLastError=true)]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, SetWindowPosFlags uFlags);
I'm trying to figure out how this code works but I just can not figure out what makes it click through.
Yes, this code is not mine since i'm trying to learn/understand it.
Assume I want the tranparancy but not the click through what needs to be changed and why?
I have been over the Windows styles pages over and over and still can not get my head around the click through part.
using System;
using System.Runtime.InteropServices;
using UnityEngine;
public class TransparentWindow : MonoBehaviour
{
[SerializeField]
private Material m_Material;
private struct MARGINS
{
public int cxLeftWidth;
public int cxRightWidth;
public int cyTopHeight;
public int cyBottomHeight;
}
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
[DllImport("user32.dll")]
private static extern int SetWindowLong(IntPtr hWnd, int nIndex, uint dwNewLong);
[DllImport("user32.dll")]
static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "SetLayeredWindowAttributes")]
static extern int SetLayeredWindowAttributes(IntPtr hwnd, int crKey, byte bAlpha, int dwFlags);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
private static extern int SetWindowPos(IntPtr hwnd, int hwndInsertAfter, int x, int y, int cx, int cy, int uFlags);
[DllImport("Dwmapi.dll")]
private static extern uint DwmExtendFrameIntoClientArea(IntPtr hWnd, ref MARGINS margins);
const int GWL_STYLE = -16;
const uint WS_POPUP = 0x80000000;
const uint WS_VISIBLE = 0x10000000;
const int HWND_TOPMOST = -1;
void Start()
{
#if !UNITY_EDITOR // You really don't want to enable this in the editor..
int fWidth = Screen.width;
int fHeight = Screen.height;
var margins = new MARGINS() { cxLeftWidth = -1 };
var hwnd = GetActiveWindow();
SetWindowLong(hwnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
// Transparent windows with click through
SetWindowLong(hwnd, -20, 524288 | 32);//GWL_EXSTYLE=-20; WS_EX_LAYERED=524288=&h80000, WS_EX_TRANSPARENT=32=0x00000020L
SetLayeredWindowAttributes(hwnd, 0, 255, 2);// Transparency=51=20%, LWA_ALPHA=2
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, fWidth, fHeight, 32 | 64); //SWP_FRAMECHANGED = 0x0020 (32); //SWP_SHOWWINDOW = 0x0040 (64)
DwmExtendFrameIntoClientArea(hwnd, ref margins);
#endif
}
void OnRenderImage(RenderTexture from, RenderTexture to)
{
Graphics.Blit(from, to, m_Material);
}
}
This function:
SetWindowLong(hwnd, -20, 524288 | 32);
does the trick. Windows implements the rule that Mircosoft made which is that a window that is transparent to the user must be transparent to the mouse.
With the transparency bit set WS_EX_TRANSPARENT the window becomes transparent to mouse too and click passes to the painted layer behind the transparent window.
You need not understand but make use of this 'OS feature' which was probably implemented to cover for something else.
Read this article about the subject and this answer that explains the parameters
So I am trying to update a combobox in another application programmatically. The application I am updating has a combobox, some textboxes, and a number of checkboxes. Depending on the item selected in the combobox certain checkboxes will become enabled or disabled. The problem I am having is that, although I can change the current item in the combobox, the gui doesn't seem to be updated the checkboxes accordingly.
Here is my code
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
const int CB_SETCURSEL = 0x014E;
static void Main(string[] args)
{
var startinfo = new ProcessStartInfo("path");
// Get an object that contains all the process resources
Process rtugen = Process.Start(startinfo);
// wait until the process has a main window handle
while (rtugen.MainWindowHandle == IntPtr.Zero)
{
rtugen.Refresh();
}
IntPtr hWnd = rtugen.MainWindowHandle;
IntPtr controllerType = FindWindowEx(hWnd, IntPtr.Zero, "ComboBox", null);
SendMessage(controllerType, CB_SETCURSEL, 12, "");
int send_cbn_selchange = MakeWParam((int)controllerType, CBN_SELCHANGE);
int i = SendMessage(hWnd, 0x111, send_cbn_selchange,0);
}
static int MakeWParam(int loWord, int hiWord)
{
return (loWord & 0xFFFF) + ((hiWord & 0xFFFF) << 16);
}
So, the above code does update the combobox, but the GUI doesn't seem to update the checkboxes after the update is made. I monitored the combobox change in spy++ and it showed me the same command being sent. Any ideas on what is going wrong?
Eventually I found this solution online to change the value of the combobox so that the application recognizes the change. Once you have a handle on the combobox you can use the WM_KEYDOWN msg to simulate key down action.
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
[DllImport("user32.dll")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
const int CB_SETCURSEL = 0x014E;
const int VK_DOWN = 0x28;
const int WM_KEYDOWN = 0x0100;
static void Main(string[] args)
{
var startinfo = new ProcessStartInfo("path");
// Get an object that contains all the process resources
Process rtugen = Process.Start(startinfo);
// wait until the process has a main window handle
while (rtugen.MainWindowHandle == IntPtr.Zero)
{
rtugen.Refresh();
}
IntPtr hWnd = rtugen.MainWindowHandle;
IntPtr controllerType = FindWindowEx(hWnd, IntPtr.Zero, "ComboBox", null);
SendMessage(controllerType, CB_SETCURSEL, 0, "");
for (int i = 0; i < Index; i++)
SendMessage(controllerType, WM_KEYDOWN, VK_DOWN, 0);
}
i need to open a word document in a panel control of Windows Forms Application to view/edit file and save.
i use this statement :
[DllImport("user32.dll")]
public static extern int FindWindow(string strclassName, string strWindowName);
[DllImport("user32.dll")]
static extern int SetParent(int hWndChild, int hWndNewParent);
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
static extern bool SetWindowPos(
int hWnd, // handle to window
int hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags // window-positioning options
);
[DllImport("user32.dll", EntryPoint = "MoveWindow")]
static extern bool MoveWindow(
int hWnd,
int X,
int Y,
int nWidth,
int nHeight,
bool bRepaint
);
const int SWP_DRAWFRAME = 0x20;
const int SWP_NOMOVE = 0x2;
const int SWP_NOSIZE = 0x1;
const int SWP_NOZORDER = 0x4;
const int SWP_FRAMECHANGED = 0x20;
ToolsComponents.MSWord word = new ToolsComponents.MSWord();
private void toolStripButton2_Click(object sender, EventArgs e)
{
word.CreateWordDocument();
word.OpenFile(#"C:\Users\ME\Documents\test.docx", true);
int wordWnd = FindWindow("Opusapp", null);
if (wordWnd != 0)
{
int ret = SetParent(wordWnd, pnlShowForm.Handle.ToInt32());
//int ret2 = FindWindow("Opusapp", null);
//ret = SetParent(wordWnd, pnlShowForm.Handle.ToInt32());
SetWindowPos(wordWnd, pnlShowForm.Handle.ToInt32(), 0, 0, pnlShowForm.Bounds.Width - 20, pnlShowForm.Bounds.Height - 20, SWP_FRAMECHANGED | SWP_NOZORDER | SWP_NOMOVE | SWP_DRAWFRAME);
MoveWindow(wordWnd, -5, -33, pnlShowForm.Bounds.Width + 10, pnlShowForm.Bounds.Height + 57, true);
}
}
private void frmDocumentManager_FormClosing(object sender, FormClosingEventArgs e)
{
if (word != null)
{
word.CloseDoc(true);
word.Quit();
}
but this is not a good solution and have problem in runtime.
in sometimes MS word and document started outside the form and i can't control this.
I'm opening up a new Outlook email from code in my windows application. Is there a way to give focus to the new window (instead of blinking orange behind)?
Thank you!
Try using the PInvoke:
public class MoveToForeground
{
[DllImportAttribute("User32.dll")]
private static extern int FindWindow(string ClassName, string WindowName);
[DllImport("user32.dll", EntryPoint="SetWindowPos")]
public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
const int SWP_NOMOVE = 0x0002;
const int SWP_NOSIZE = 0x0001;
const int SWP_SHOWWINDOW = 0x0040;
const int SWP_NOACTIVATE = 0x0010;
public static void DoOnProcess(string processName)
{
var process = Process.GetProcessesByName(processName);
if (process.Length > 0)
{
int hWnd = FindWindow(null, process[0].MainWindowTitle.ToString());
SetWindowPos(new IntPtr(hWnd), 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
}
}
}
MoveToForeground.DoOnProcess("OUTLOOK.EXE");