Is there a way to embed a WebDriver driver to a WPF window, similarly to the WPF's WebBrowser control?
Optionally, is there a way to use Selenium on the WebBrowser control itself?
So far, it's only possible to create a new WebDriver window, separate from any other WPF window in the application.
This was a slightly trick one ;)
As Selenium uses external applications (the browsers) most of the time there is no "native" solution like just integrating the Browser-UI in an app.
There are, however, Windows-specific APIs to achieve exactly that with WinForms.
UnsafeNativeMethods.cs
private static class UnsafeNativeMethods {
[DllImport("user32")]
public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);
}
Basically the idea is to "wrap" the external browser within a usercontrol in your app.
SeleniumHost.cs
public void AttachDriverService(DriverService service) {
//get the process started by selenium
var driverProcess = Process.GetProcessById(service.ProcessId);
//find the first child-process (should be the browser)
var browserProcess = driverProcess.GetChildren()
.Where(p => p.ProcessName != "conhost")
.First();
_BrowserHandle = browserProcess.MainWindowHandle;
//set the parent window utilizing Win32-API
UnsafeNativeMethods.SetParent(_BrowserHandle.Value, this.Handle);
//handle moving/resizing of your appo to be reflected on browser
UnsafeNativeMethods.MoveWindow(_BrowserHandle.Value, 0, 0, Width, Height, true);
this.Resize += (sender, e) => {
UnsafeNativeMethods.MoveWindow(_BrowserHandle.Value, 0, 0, Width, Height, true);
};
}
At last the extension used to enumerate child-processes via WMI:
ProcessExtensions.cs
public static IEnumerable<Process> GetChildren(this Process parent) {
var query = new ManagementObjectSearcher($#"
SELECT *
FROM Win32_Process
WHERE ParentProcessId={parent.Id}");
return from item in query.Get().OfType<ManagementBaseObject>()
let childProcessId = (int)(UInt32)item["ProcessId"]
select Process.GetProcessById(childProcessId);
}
and call the method like:
var host = new SeleniumHost();
var service = InternetExplorerDriverService.CreateDefaultService();
var driver = new InternetExplorerDriver(service);
host.AttachDriverService(service);
When done, this solves the WinForms-Part. To integrate this in WPF you need to leverage WindowsFormsHost to display the WinForms-Control.
Check out my fresh published repo on GitHub for further reference or directly leverage the NuGet-Package.
Please bear with me, as those are very hot bits - so there sure will be bugs and further improvements to make in the future (like removing the chrome/border from the browser). Hopefully you can get the idea and/or maybe contribute on GitHub.
Related
I have a winforms application (.net 5.0) that comprises two forms - one for the operator (to setup different options/enter data, admin duties) and another for the user to interact with (play games, follow instructions etc). Each form is displayed on separate monitors, with both visible/available when the application is run.
One requirement of the application is to run external applications (games) in the user form. The user form contains a panel (as a header) and several custom user controls. One of the user controls becomes the parent of the external application.
Using the code below I am able to run external applications inside the user form. However, the applications all start outside of the form (as indicated by the appearance of a 'splash' screen) before being moved inside the user form using SetParent(...).
What I want to achieve is for the 'splash' screen not to appear before moving the external application to the user control. I understand that causes/solutions may range depending on the application in question, so guidance is welcomed in lieu of a solution.
Much of the code below has been sourced from SO and Google more widely, however I have been unable to find references to 'splash' screen issues.
public static int GWL_STYLE = -16;
public static int WS_BORDER = 0x00800000; //window with border
public static int WS_DLGFRAME = 0x00400000; //window with double border but no title
public static int WS_CAPTION = WS_BORDER | WS_DLGFRAME; //window with a title bar
public const uint WS_SIZEBOX = 0x00040000;
...
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
[DllImport("user32.dll")]
static extern bool MoveWindow(IntPtr Handle, int x, int y, int w, int h, bool repaint);
[DllImport("user32.dll")]
static extern bool RemoveMenu(IntPtr hMenu, uint uPosition, uint uFlags);
public static void HideWindowBorders(IntPtr hWnd)
{
var style = GetWindowLong(hWnd, GWL_STYLE); //gets current style
SetWindowLong(hWnd, GWL_STYLE, (uint)(style & ~(WS_CAPTION | WS_SIZEBOX))); //removes caption and the sizebox from current style
}
...
// Button click in the operator form starts the external application
private void playSuperTuxBtn_Click(object sender, EventArgs e)
{
Process superTux = new Process();
superTux.StartInfo.FileName = #"C:\Program Files\SuperTux\bin\supertux2.exe"; // 0.6.3
superTux.StartInfo.UseShellExecute = false;
superTux.StartInfo.CreateNoWindow = false;
superTux.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
superTux.Start();
superTux.WaitForInputIdle();
while (superTux.MainWindowHandle == IntPtr.Zero)
{
Thread.Sleep(100);
superTux.Refresh();
}
RemoveMenuBar(superTux.MainWindowHandle);
HideWindowBorders(superTux.MainWindowHandle);
SetParent(superTux.MainWindowHandle, RebotControlForm.uiForm.conUIGamePlay.Handle);
MoveWindow(superTux.MainWindowHandle, 0, 0, RebotControlForm.uiForm.conUIGamePlay.Width, RebotControlForm.uiForm.conUIGamePlay.Height, true);
}
MainWindowHandle is not an actual thing in Windows, it is faked by .NET.
To catch all windows you would have to use a hook of some kind, SetWinEventHook or a CBT hook.
Is there a way to detect if the windows/os language changed even when my app is not in focus?
So far I was able to achieve what I wanted only if the app was focused using:
string language = "";
System.Windows.Input.InputLanguageManager.Current.InputLanguageChanged +=
new System.Windows.Input.InputLanguageEventHandler((sender, e) =>
{
language = e.NewLanguage.DisplayName;
MessageBox.Show(language);
});
But as you can understand, this is not exactly what I want..
I was thinking about other solution such as hooking the keys that change the language (for example alt+shift) but I wont be able to know what language is currently in use and a user can change the default hotkey...
Would appreciate your help.
The problem you are facing is related with how WM_INPUTLANGCHANGE message works. This message is sent to programs by operating system in order to inform them about language changes. However, according to documentation this message is sent only to "to the topmost affected window". It means that you can even call a native method GetKeyboardLayout (it is used by InputLanguageManager by the way) but if an application is not active GetKeyboardLayout will always return the last known, outdated, language.
Taking this into account it might be a good idea to use the solution pointed by #VDohnal i.e. find the current topmost window and read keyboard layout for it. Here is a quick proof of concept how to do it inside WPF application. I used an additional thread that periodically finds the topmost window and ready keyboard layout for it. The code is far from being perfect but it works and it might help you to implement your own solution.
public partial class MainWindow : Window
{
[DllImport("user32.dll")]
static extern IntPtr GetKeyboardLayout(uint idThread);
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
private CultureInfo _currentLanaguge;
public MainWindow()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
while (true)
{
HandleCurrentLanguage();
Thread.Sleep(500);
}
});
}
private static CultureInfo GetCurrentCulture()
{
var l = GetKeyboardLayout(GetWindowThreadProcessId(GetForegroundWindow(), IntPtr.Zero));
return new CultureInfo((short)l.ToInt64());
}
private void HandleCurrentLanguage()
{
var currentCulture = GetCurrentCulture();
if (_currentLanaguge == null || _currentLanaguge.LCID != currentCulture.LCID)
{
_currentLanaguge = currentCulture;
MessageBox.Show(_currentLanaguge.Name);
}
}
}
I have a desktop application installed on my machine. When I start a program some kind of window gets open. let's say, something like this (just example):
So, I want to write an application in C# that will find this window and capture some data from it.
What tools should I look at? I want to go with a path of least resistance.
I need to capture images, text from textboxes, and also find controls by text and click on them.
I suggest you use the cool but little-known UI Automation API for this work.
For this, the first thing to test is launch the associated UISpy tool. It will display a tree of all accessible windows on screen. It also is able to run some actions like pressing a menu, selecting an item, etc. This is using what's called UI Automation Control Patterns, which provide a way to categorize and expose a control's functionality independent of the control type or the appearance of the control.
So, if you can automate this application with UI Spy, you also can do the exact same thing using .NET code (UISpy is itself simply using the underlying API).
Here is an interesting tutorial article about UI automation programming: The Microsoft UI Automation Library
You should start enumerating handles of all windows for that process :
https://stackoverflow.com/a/2584672/351383
Then for each handle get information about text and position, with position infomation you can take screenshots of desktop on that position to get images AFAIK there is no other way to get images from a window of running application.
When you got screen positions of the controls then use from link below to simulate left mouse click, search windows for some text and then click on some point inside control, here is the method that will click a point :
https://stackoverflow.com/a/10355905/351383
I put toghether quick class to gather that data for process :
public static class ProcessSpy
{
public static List<ProcessSpyData> GetDataForProcess(string processName)
{
var result = new List<ProcessSpyData>();
Process myProc = Process.GetProcessesByName(processName).FirstOrDefault();
if (myProc != null)
{
var myHandles = EnumerateProcessWindowHandles(myProc);
foreach (IntPtr wndHandle in myHandles)
{
result.Add(new ProcessSpyData(wndHandle));
}
}
return result;
}
delegate bool EnumThreadDelegate(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll")]
static extern bool EnumThreadWindows(int dwThreadId, EnumThreadDelegate lpfn, IntPtr lParam);
static IEnumerable<IntPtr> EnumerateProcessWindowHandles(Process prc)
{
var handles = new List<IntPtr>();
foreach (ProcessThread thread in prc.Threads)
EnumThreadWindows(thread.Id, (hWnd, lParam) => { handles.Add(hWnd); return true; }, IntPtr.Zero);
return handles;
}
}
public class ProcessSpyData
{
private const uint WM_GETTEXT = 0x000D;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);
[DllImport("user32.dll")]
private static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect);
[StructLayout(LayoutKind.Sequential)]
private struct RECT
{
int left, top, right, bottom;
public Rectangle ToRectangle()
{
return new Rectangle(left, top, right - left, bottom - top);
}
}
[DllImport("user32.dll")]
static extern bool ClientToScreen(IntPtr hWnd, ref Point lpPoint);
public IntPtr WindowHandle { get; private set; }
public string WindowText { get; private set; }
public Rectangle ClientRect { get; private set; }
public Rectangle ScreenPos { get; private set; }
public ProcessSpyData(IntPtr windowHandle)
{
this.WindowHandle = windowHandle;
GetWindowText();
GetWindowSize();
}
private void GetWindowText()
{
StringBuilder message = new StringBuilder(1024);
SendMessage(this.WindowHandle, WM_GETTEXT, message.Capacity, message);
this.WindowText = message.ToString();
}
private void GetWindowSize()
{
var nativeRect = new RECT();
GetClientRect(this.WindowHandle, out nativeRect);
this.ClientRect = nativeRect.ToRectangle();
Point loc = this.ClientRect.Location;
ClientToScreen(this.WindowHandle, ref loc);
this.ScreenPos = new Rectangle(loc, this.ClientRect.Size);
}
}
That should get you started, but you have to be aware if app is using non standard controls then there is no way to get text out of it with this method, and for images maybe you will get better results looking at executable resources.
UPDATE
Geting controls text for various control types (MFC, winforms, Delphi VCL etc.) would be very hard task, but for winforms see excelent Managed Windows API, they even have some sort of spy application in tools, look at that.
What kind of data are you trying to capture?
You may try listening to windows messages or reading the memory.
Depending on how much of these type of tasks you are going to be doing in the future (or how important this one is) you could try investing in something like Ranorex Spy (Ranorex studio is ott).
Link: http://www.ranorex.com/product/tools/ranorex-spy.html
there is no other way than to inject the application you want to inspect. This is how UISpy actually runs. This is also why UISpy should be run with Administrative credential.
I'm adding some code to an app that will launch another app if it isn't already running, or if it is, bring it to the front. This requires a small amount of interop/WinAPI code, which I've gotten examples for from other sites but can't seem to get to work in Win7.
If the window is in some visible state, then the API's SetForegroundWindow method works like a treat (and this would be the main case, as per company policy if the external app is running it should not be minimized). However, if it is minimized (exceptional but important as my app will appear to do nothing in this case), neither this method nor ShowWindow/ShowWindowAsync will actually bring the window back up from the taskbar; all of the methods simply highlight the taskbar button.
Here's the code; most of it works just fine, but the call to ShowWindow() (I've also tried ShowWindowAsync) just never does what I want it to no matter what the command I send is:
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hWnd);
private const int SW_SHOWNORMAL = 1;
private const int SW_SHOWMAXIMIZED = 3;
private const int SW_RESTORE = 9;
[DllImport("user32.dll")]
private static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
...
//The app is named uniquely enough that it can't be anything else,
//and is not normally launched except by this one.
//so this should normally return zero or one instance
var processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Any()) //a copy is already running
{
//I can't currently tell the window's state,
//so I both restore and activate it
var handle = processes.First().MainWindowHandle;
ShowWindow(handle, SW_RESTORE); //GRR!!!
SetForegroundWindow(handle);
return true;
}
try
{
//If a copy is not running, start one.
Process.Start(#"C:\Program Files (x86)\ExternalApp\ExternalApp.exe");
return true;
}
catch (Exception)
{
//fallback for 32-bit OSes
Process.Start(#"C:\Program Files\ExternalApp\ExternalApp.exe");
return true;
}
I've tried SHOWNORMAL (1), SHOWMAXIMIZED (3), RESTORE (9), and a couple other sizing commands, but nothing seems to do the trick. Thoughts?
EDIT: I found an issue with some of the other code I had thought was working. The call to GetProcessesByName() was not finding the process because I was looking for the executable name, which was not the process name. That caused the code I thought was running and failing to actually not execute at all. I thought it was working because the external app will apparently also detect that a copy is already running and try to activate that current instance. I dropped the ".exe" from the process name I search for and now the code executes; however that seems to be a step backwards, as now the taskbar button isn't even highlighted when I call ShowWindow[Async]. So, I now know that neither my app, nor the external app I'm invoking, can change the window state of a different instance programmatically in Win7. What's goin' on here?
Working code using FindWindow method:
[DllImport("user32.dll")]
public static extern IntPtr FindWindow(string className, string windowTitle);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, ShowWindowEnum flags);
[DllImport("user32.dll")]
private static extern int SetForegroundWindow(IntPtr hwnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowPlacement(IntPtr hWnd, ref Windowplacement lpwndpl);
private enum ShowWindowEnum
{
Hide = 0,
ShowNormal = 1, ShowMinimized = 2, ShowMaximized = 3,
Maximize = 3, ShowNormalNoActivate = 4, Show = 5,
Minimize = 6, ShowMinNoActivate = 7, ShowNoActivate = 8,
Restore = 9, ShowDefault = 10, ForceMinimized = 11
};
private struct Windowplacement
{
public int length;
public int flags;
public int showCmd;
public System.Drawing.Point ptMinPosition;
public System.Drawing.Point ptMaxPosition;
public System.Drawing.Rectangle rcNormalPosition;
}
private void BringWindowToFront()
{
IntPtr wdwIntPtr = FindWindow(null, "Put_your_window_title_here");
//get the hWnd of the process
Windowplacement placement = new Windowplacement();
GetWindowPlacement(wdwIntPtr, ref placement);
// Check if window is minimized
if (placement.showCmd == 2)
{
//the window is hidden so we restore it
ShowWindow(wdwIntPtr, ShowWindowEnum.Restore);
}
//set user's focus to the window
SetForegroundWindow(wdwIntPtr);
}
You can use it by calling BringWindowToFront().
I always have one instance of the application running so if you can have several open instances simultaneously you might want to slightly change the logic.
... Apparently you cannot trust the information a Process gives you.
Process.MainWindowHandle returns the window handle of the first window created by the application, which is USUALLY that app's main top-level window. However, in my case, a call to FindWindow() shows that the handle of the actual window I want to restore is not what MainWindowHandle is pointing to. It appears that the window handle from the Process, in this case, is that of the splash screen shown as the program loads the main form.
If I call ShowWindow on the handle that FindWindow returned, it works perfectly.
What's even more unusual is that when the window's open, the call to SetForegroundWindow(), when given the process's MainWindowHandle (which should be invalid as that window has closed), works fine. So obviously that handle has SOME validity, just not when the window's minimized.
In summary, if you find yourself in my predicament, call FindWindow, passing it the known name of your external app's main window, to get the handle you need.
I had the same problem. The best solution I have found is to call ShowWindow with the flag SW_MINIMIZE, and then with SW_RESTORE. :D
Another possible solution:
// Code to display a window regardless of its current state
ShowWindow(hWnd, SW_SHOW); // Make the window visible if it was hidden
ShowWindow(hWnd, SW_RESTORE); // Next, restore it if it was minimized
SetForegroundWindow(hWnd); // Finally, activate the window
from comments at: http://msdn.microsoft.com/en-us/library/ms633548%28VS.85%29.aspx
Tray calling ShowWindow(handle, SW_RESTORE); after SetForegroundWindow(handle);
This might solve your problem.
It sounds like you're trying to perform an action that has the same result as alt-tabbing, which brings the window back if it was minimized while "remembering" if it was maximized.
NativeMethods.cs:
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
// Specify your namespace here
namespace <your.namespace>
{
static class NativeMethods
{
// This is the Interop/WinAPI that will be used
[DllImport("user32.dll")]
static extern void SwitchToThisWindow(IntPtr hWnd, bool fUnknown);
}
}
Main code:
// Under normal circumstances, only one process with one window exists
Process[] processes = Process.GetProcessesByName("ExternalApp.exe");
if (processes.Length > 0 && processes[0].MainWindowHandle != IntPtr.Zero)
{
// Since this simulates alt-tab, it restores minimized windows to their previous state
SwitchToThisWindow(process.MainWindowHandle, true);
return true;
}
// Multiple things are happening here
// First, the ProgramFilesX86 variable automatically accounts for 32-bit or 64-bit systems and returns the correct folder
// Secondly, $-strings are the C# shortcut for string.format() (It automatically calls .ToString() on each variable contained in { })
// Thirdly, if the process was able to start, the return value is not null
try { if (Process.Start($"{System.Environment.SpecialFolder.ProgramFilesX86}\\ExternalApp\\ExternalApp.exe") != null) return true; }
catch
{
// Code for handling an exception (probably FileNotFoundException)
// ...
return false;
}
// Code for when the external app was unable to start without producing an exception
// ...
return false;
I hope this provides a much simpler solution.
(General Rule: If a string value is ordinal, i.e. it belongs to something and isn't just a value, then it is better to get it programmatically. You'll save yourself a lot of trouble when changing things. In this case, I'm assuming that the install location can be converted to a global constant, and the .exe name can be found programmatically.)
I know its too late, still my working code is as follows so that someone later can get quick help :)
using System.Runtime.InteropServices;
using System.Diagnostics;
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);
[DllImport("user32.dll", EntryPoint = "FindWindow")]
public static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
private static void ActivateApp(string processName)
{
Process[] p = Process.GetProcessesByName(processName);
if (p.Length > 0)
{
IntPtr handle = FindWindowByCaption(IntPtr.Zero, p[0].ProcessName);
ShowWindow(handle, 9); // SW_RESTORE = 9,
SetForegroundWindow(handle);
}
}
ActivateApp(YOUR_APP_NAME);
Actually, FindWindowByCaption is the key here, this method collects the window handle correctly when app is running silently in the system tray and also when app is minimized.
I am writing a .Net COM DLL that runs inside a native Windows application.
I am attempting to inject an additional pane into this app's statusbar, and it does not have any specific implementation to do so, so I am trying to subclass the app's statusbar myself.
I am using the Win32 API SetParent() to switch the parent of a label control from a .Net form to the msctls_statusbar32 instance. I used a label because it is the closest implementation to a native "static" class control that I could find without writing my own control.
Somehow I've even managed to get NativeWindow to successfully hook in to both the statusbar and my label's messages (though at the moment it just passes them all to the next WndProc), and I've assigned matching styles and styleExs to my label's window, and I can see my label as a child with the msctls_statusbar32 as its parent. Everything looks like it should be working correctly, but it's not. My control does not show up in the parent app's statusbar.
What I don't understand is why it is not showing up. Nearly everything I can think of matches correctly -- granted, the class for my label is "WindowsForms10.STATIC.app.0.378734a" and not "static", but other than that it is on the correct process and thread, has matching window styles (at least the hex value... Spy++ seems to enumerate them differently), and for all purposes pretty much blends in with the rest of the controls. Would anybody know what else needs to be done to get it to be visible?
(I had originally gone the route of CreateWindowEx and setting WNDPROC callbacks but I could not get the app to work... it would freeze for a minute or so and then unfreeze, and I would notice my window disappeared from the window tree)
Thank you!
you can try working with existing status bar control; what you can do is to reset text to an existing section of it or add a new one; also you would probably need to set up new widths to existing sections of the statusbar. You can find details on how to work with statusbar control here:msdn Status Bars
Please find an example of how you could do it below. I actually tried it with c# com object used by win32 application and it seem to work fine for me.
[ComVisible(true)]
[Guid("CC5B405F-F3CD-417E-AA00-4638A12A2E94"),
ClassInterface(ClassInterfaceType.None)]
public class TestInterface : ITestInterface // see declaration of the interface below
{
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, IntPtr windowTitle);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, int wParam, IntPtr lParam);
public const int SB_SETTEXT = 1035;
public const int SB_SETPARTS = 1028;
public const int SB_GETPARTS = 1030;
public unsafe void Test()
{
IntPtr mainWindowHandle = Process.GetCurrentProcess().MainWindowHandle;
// find status bar control on the main window of the application
IntPtr statusBarHandle = FindWindowEx(mainWindowHandle, IntPtr.Zero, "msctls_statusbar32", IntPtr.Zero);
if (statusBarHandle != IntPtr.Zero)
{
// set text for the existing part with index 0
IntPtr text = Marshal.StringToHGlobalAuto("test text 0");
SendMessage(statusBarHandle, SB_SETTEXT, 0, text);
Marshal.FreeHGlobal(text);
// create new parts width array
int nParts = SendMessage(statusBarHandle, SB_GETPARTS, 0, IntPtr.Zero).ToInt32();
nParts++;
IntPtr memPtr = Marshal.AllocHGlobal(sizeof(int) * nParts);
int partWidth = 100; // set parts width according to the form size
for (int i = 0; i < nParts; i++)
{
Marshal.WriteInt32(memPtr, i*sizeof(int), partWidth);
partWidth += partWidth;
}
SendMessage(statusBarHandle, SB_SETPARTS, nParts, memPtr);
Marshal.FreeHGlobal(memPtr);
// set text for the new part
IntPtr text0 = Marshal.StringToHGlobalAuto("new section text 1");
SendMessage(statusBarHandle, SB_SETTEXT, nParts-1, text0);
Marshal.FreeHGlobal(text0);
}
}
}
[ComVisible(true)]
[Guid("694C1820-04B6-4988-928F-FD858B95C880")]
public interface ITestInterface
{
[DispId(1)]
void Test();
}
hope this helps, regards
Many possible reasons:
The .Net Label control blows up when it finds it does not have a WinForms parent.
The native Status bar is drawing over the label control becuase of incorrect Z-Order.
The Label control is not visible.
As it turns out the answer was dumbfoundingly simple... the label's X and Y coords were out of the display area of the statusbar parent. Moving them to (0, 0) and it shows up right there! Of course, now the problems have moved on to: C# WinForms control in .Net COM Server won't redraw