C#: How to tell if an EXE has an icon? - c#

I'm looking for a way to tell whether or not an EXE file contains an application icon. From the answer here, I tried this:
bool hasIcon = Icon.ExtractAssociatedIcon(exe) != null;
But this seems to work even if the EXE has no icon. Is there a way to detect this in .NET?
edit: I'm OK with solutions involving P/Invoke.

You can get the IDI_APPLICATION icon through SystemIcons.Application property from SystemIcons class
if (Icon.ExtractAssociatedIcon(exe).Equals(SystemIcons.Application))
{
...
}
See MSDN for more details.

Try this. Define your pinvoke like this:
[DllImport("user32.dll")]
internal static extern IntPtr LoadImage(IntPtr hInst, IntPtr name, uint type, int cxDesired, int cyDesired, uint fuLoad);
[DllImport("kernel32.dll")]
static extern bool EnumResourceNames(IntPtr hModule, int dwID, EnumResNameProcDelegate lpEnumFunc, IntPtr lParam);
delegate bool EnumResNameProcDelegate(IntPtr hModule, IntPtr lpszType, IntPtr lpszName, IntPtr lParam);
[DllImport("kernel32.dll", SetLastError = true)]
internal static extern IntPtr LoadLibraryEx(string name, IntPtr handle, uint dwFlags);
private const int LOAD_LIBRARY_AS_DATAFILE = 0x00000002;
private const int LOAD_LIBRARY_AS_IMAGE_RESOURCE = 0x00000020;
private const int IMAGE_ICON = 1;
private const int RT_GROUP_ICON = 14;
Then you can write a function like this:
static bool HasIcon(string path)
{
// This loads the exe into the process address space, which is necessary
// for LoadImage / LoadIcon to work note, that LOAD_LIBRARY_AS_DATAFILE
// allows loading a 32-bit image into 64-bit process which is otherwise impossible
IntPtr moduleHandle = LoadLibraryEx(path, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
if (moduleHandle == IntPtr.Zero)
{
throw new ApplicationException("Cannot load executable");
}
IntPtr index = IntPtr.Zero;
bool hasIndex = false;
bool enumerated = EnumResourceNames(moduleHandle, RT_GROUP_ICON, (module, type, name, param) =>
{
index = name;
hasIndex = true;
// Only load first icon and bail out
return false;
}, IntPtr.Zero);
if (!enumerated || !hasIndex)
{
return false;
}
// Strictly speaking you do not need this you can return true now
// This is to demonstrate how to access the icon that was found on
// the previous step
IntPtr result = LoadImage(moduleHandle, index, IMAGE_ICON, 0, 0, 0);
if (result == IntPtr.Zero)
{
return false;
}
return true;
}
It has added bonus that if you want to, after LoadImage you can load the icon with
Icon icon = Icon.FromHandle(result);
and do whatever you want with that.
Important note: I have not done any clean up in the function, so you cannot use it as is, you'll leak handles/memory. Proper clean up is left as an exercise for the reader. Read the description of every of the winapi function used in MSDN and call corresponding clean up functions as needed.
An alternate way using shell32 api can be found here, although I don't know if it has the same problem you encountered.
Also, old, but still very relevant article: https://msdn.microsoft.com/en-us/library/ms997538.aspx

Related

Evaluate if drive is in use

I'd like to evaluate wether a drive is in use (if I'm not mistaken this means that some read/write stuff is happening with the drive) using C#. I wouldn't mind for a solution using bash scripts or similiar either, as I could use them in a C# application. I already found a question regarding bash scripts here, but couldn't solve my problem with the answers given.
I considered to use the DriveInfo class already, however it didn't seem to have anything useful for me. I wondered wether I could use the IsReady property from DriveInfo, because I guessed that it wouldn't be ready while it is read/written, but this attempt seems rather botchy to me.
However I still tried it:
private static bool IsDriveInUse ()
{
var drive = DriveInfo.GetDrives ().FirstOrDefault(info => info.Name.StartsWith(DRIVE_LETTER.ToString()));
return drive != null && !drive.IsReady;
}
But it didn't work (it returned false while I played music from my drive).
An optimal solution for me would be a function that tells me wether the drive was in use in a specific span of time (let's stick to the name IsDriveInUse). That means that if the time was for example 60 seconds, IsDriveInUse should return true if 5 seconds before the function call content from the drive was read and false if there was no read/write action in the passed 60 seconds.
EDIT To specify what exactly I mean by in use, I'll try to explain what I'm trying to do. I am writing a tool, which automatically spins down my hard drive, when it's been idle or when I press a keyboard shortcut. I managed to spin it down programmatically (even though either the windows integrated tool nor other tools I found were able to do that, but that's another problem). However, it now spins down the hard drive every minute, regardless of wether it's currently in use or not. That means, if I play music from my hard drive, it's still spinned down, just to spin up directly after it, which doesn't decrease noise development.
I hope this clarified the matter.
EDIT I now tried using the FSCTL_LOCK_VOLUME control code (I couldn't find a value for IOCTL_DISK_PERFORMANCE), but it still returned false for IsDriveInUse() while I was playing music. Furthermore it caused windows to directly spin the drive up again as I spinned it down (probably because the releasing made Windows think that something was using the drive). This is what I tried:
public class DriveManager
{
public const int FSCTL_LOCK_VOLUME = 0x00090018;
public const int FSCTL_UNLOCK_VOLUME = 0x0009001c;
[DllImport ("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CreateFile (
string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes,
uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile);
[return: MarshalAs (UnmanagedType.Bool)]
[DllImport ("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool DeviceIoControl (
[In] SafeFileHandle hDevice,
[In] int dwIoControlCode, [In] IntPtr lpInBuffer,
[In] int nInBufferSize, [Out] IntPtr lpOutBuffer,
[In] int nOutBufferSize, out int lpBytesReturned,
[In] IntPtr lpOverlapped);
public static SafeFileHandle CreateFileR (string device)
{
string str = device.EndsWith (#"\") ? device.Substring (0, device.Length - 1) : device;
return new SafeFileHandle (
CreateFile (#"\\.\" + str, WinntConst.GENERIC_READ, WinntConst.FILE_SHARE_READ, IntPtr.Zero,
WinntConst.OPEN_EXISTING, WinntConst.FILE_ATTRIBUTE_NORMAL, IntPtr.Zero), true);
}
internal class WinntConst
{
// Fields
internal static uint FILE_ATTRIBUTE_NORMAL = 0x80;
internal static uint FILE_SHARE_READ = 1;
internal static uint GENERIC_READ = 0x80000000;
internal static uint OPEN_EXISTING = 3;
}
public static bool IsDriveInUse (string deviceName)
{
var handle = CreateFileR (deviceName);
var buffer = Marshal.AllocHGlobal (sizeof (int));
try
{
return
DeviceIoControl (handle,
FSCTL_LOCK_VOLUME,
IntPtr.Zero,
0,
buffer,
sizeof(int),
out var bytesReturned,
IntPtr.Zero
);
}
finally
{
var sessionId = Marshal.ReadInt32 (buffer);
Marshal.FreeHGlobal (buffer);
handle.Close ();
}
}
And the implementation:
private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($#"{DRIVE_LETTER}:\");
Maybe it helps to see the part in which I'm spinning the disc down as well (I used Smartmontools for this):
internal static class Program
{
private const string PROGRAM_PATH = #"External\smartctl.exe";
private const string ARGUMENTS_SHUTDOWN = #"-s standby,now {0}:";
private const char DRIVE_LETTER = 'd';
public static void Main (string [] args)
{
InitializeHotKey ();
Console.WriteLine ("Hotkey registered!");
while (true)
{
Thread.Sleep (60000);
if (!IsDriveInUse ())
ShutDownDrive (true);
}
}
private static bool IsDriveInUse () => DriveManager.IsDriveInUse ($#"{DRIVE_LETTER}:\");
private static void InitializeHotKey ()
{
HotKeyManager.RegisterHotKey (Keys.D, KeyModifiers.Alt | KeyModifiers.Control);
HotKeyManager.HotKeyPressed += HotKeyPressed;
}
private static void HotKeyPressed (object sender, HotKeyEventArgs hotKeyEventArgs) => ShutDownDrive (true);
private static void ShutDownDrive (bool withDialog = false)
{
Process process;
(process = new Process
{
StartInfo = new ProcessStartInfo
{
WindowStyle = ProcessWindowStyle.Hidden,
FileName = PROGRAM_PATH,
Arguments = string.Format (ARGUMENTS_SHUTDOWN, DRIVE_LETTER)
}
}).Start ();
process.WaitForExit ();
process.Close ();
if (withDialog)
Console.WriteLine ("Drive shut down!");
}
}
Perhaps you could use the Windows Performance Counter relevant to your drive ?
"Disk Read/sec" seems quite relevant for what youhave in mind.
In .Net, the counters are available via System.Diagnostics.PerformanceCounter
see there :
https://msdn.microsoft.com/en-us/library/system.diagnostics.performancecounter(v=vs.110).aspx

Setting all checkboxes in a listView in another application with winAPI

So I have this code to set all items (or even a single item i stated the code for item number 3) for a listview in another application using sendmessage, I already managed to successfully get the item window handle of the listview and got the item count right but when i use setitemstate the other application gives me error and closes (given that there is another application I have that do this job just fine but i don't have its source code)
this is my code :
if (windowName.Contains("Invite to Room")) {
IntPtr hwndChild0 = FindWindowEx(hWnd, IntPtr.Zero, "SysListView32", "");
int itemCount = SendMessage(hwndChild0, LVM_GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
LV_ITEM lvItem = new LV_ITEM();
lvItem.Index = 3;
lvItem.SubIndex = 2;
lvItem.TextLength = 50;
lvItem.Mask = LVIF_STATE;
lvItem.State = LVIS_SELECTED;
lvItem.StateMask = LVIS_SELECTED;
SendMessage(hwndChild0, LVM_SETITEMSTATE, 3, IntPtr.Zero);
}
this is my LV_ITEM structure
public struct LV_ITEM
{
public uint Mask;
public int Index;
public int SubIndex;
public int State;
public IntPtr StateMask;
public string Text;
public int TextLength;
public int ImageIndex;
public IntPtr LParam;
}
and this is the declaration of the used (sendmessage and setitemstate)
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, string lParam);
private const uint LVM_FIRST = 0x1000;
private const uint LVM_SETITEMSTATE = (LVM_FIRST + 43);
any help with this?
Edit :
the application error has a log file that said the following message :
"The thread tried to read from or write to a virtual address for which it doesn't have the appropriate access"
Some messages only use WPARAM, LPARAM and return LRESULT to pass data around, like LVM_GETITEMCOUNT that worked for you.
Other messages use pointers to some data structure to be used or filled, like LVM_SETITEMSTATE. It expects a pointer to pre-filled LV_ITEM structure in LPARAM, while you pass IntPtr.Zero - so you cause an access violation in that other application trying to dereference that zero pointer.
However, you can't just simply pass the pointer to your lvItem, as it wouldn't make any sense in another process. You need to allocate a memory for that structure in the second process, initialize it, send you message and read that memory back (if you expect any response).

When did SECURITY_ATTRIBUTES change and why?

I have some code that uses P/Invoke to launch a process and capture the standard output. (The story of why we did this using P/Invoke instead of System.Diagnostics.Process is long and convoluted; suffice it to say it's a requirement.) It's been working in production under heavy load for nearly a year, and the tests that exercise it have always passed.
This morning though I ran the tests, and they failed. I can't be certain when I last ran the tests prior to this morning (5/15/2014), but I believe it was 4/24/2014. The tests passed then, but failed this morning. I was getting the "PInvokeStackImbalance" error message, so I did some research, and eventually realized the signature of one of the structs used by the extern method (CreatePipe in this instance) was incorrect. I changed it, and the tests started passing again.
I'm happy to have found the fix, but I'm concerned about deployment. Why did the signature of the struct change? I haven't upgraded my OS or anything - I was running Windows 7 x64 on 4/24, and I'm still running it now. (The deployment environment is Windows Server 2012.) I've installed (and uninstalled) a few apps since then, but they've been light-weight 3rd-party tools, not Microsoft or system components. I assume a Windows Update hotfix is responsible, but I can't figure out which one.
To be clear, in my own code, all I changed was this:
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public UInt32 nLength;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
to this:
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES
{
public int nLength = 12;
public IntPtr lpSecurityDescriptor = IntPtr.Zero;
public bool bInheritHandle;
}
I need to be sure that the change I made to get the code working on my machine isn't going to break the app when I deploy to production. Does anyone know how to identify what necessitated the change and how to determine whether the production environment does or does not require it?
EDIT:
Here is the code that opens the pipe for the standard output:
private PipeInfo CreatePipe()
{
PipeInfo pipeInfo = new PipeInfo();
SafeFileHandle safeFileHandle = null;
try
{
Native.SECURITY_ATTRIBUTES pipeAttributes = new Native.SECURITY_ATTRIBUTES();
pipeAttributes.bInheritHandle = true;
if (!Native.CreatePipe(out safeFileHandle, out pipeInfo.ChildHandle, pipeAttributes, 0) || safeFileHandle.IsInvalid || pipeInfo.ChildHandle.IsInvalid)
{
throw new Win32Exception();
}
if (!Native.DuplicateHandle(new HandleRef(this, Native.GetCurrentProcess()), safeFileHandle, new HandleRef(this, Native.GetCurrentProcess()), out pipeInfo.ParentHandle, 0, false, 2))
{
throw new Win32Exception();
}
}
finally
{
if (safeFileHandle != null && !safeFileHandle.IsInvalid)
{
safeFileHandle.Close();
}
}
return pipeInfo;
}
I can't exactly take credit for this code, I largely lifted it from the .NET Reference Source
Just to be clear on timeline:
May 2013 - write the CreatePipe code with the first version of SECURITY_ATTRIBUTES
June 2013 - deploy; code has been running successfully ever since
April 2014 - without any changes being made, code starts throwing stack imbalance error
May 2014 - I change to the second version of SECURITY_ATTRIBUTES and the error goes away
We encountered this problem on x64 and this post was the top result in our search. We were using the magic 12 for nLength just like your solution which we got from the C# source for process: https://referencesource.microsoft.com/#System/services/monitoring/system/diagnosticts/Process.cs
[StructLayout(LayoutKind.Sequential)]
internal class SECURITY_ATTRIBUTES {
#if !SILVERLIGHT
// We don't support ACL's on Silverlight nor on CoreSystem builds in our API's.
// But, we need P/Invokes to occasionally take these as parameters. We can pass null.
public int nLength = 12;
public SafeLocalMemHandle lpSecurityDescriptor = new SafeLocalMemHandle(IntPtr.Zero, false);
public bool bInheritHandle = false;
#endif // !SILVERLIGHT
}
It turns out CreatePipe expects a pointer, from the docs:
lpPipeAttributes
A pointer to a SECURITY_ATTRIBUTES structure that determines whether the returned handle can be inherited by child processes. If lpPipeAttributes is NULL, the handle cannot be inherited.
The solution is detailed in this stackoverflow post. It works for both x86 and x64. Our code is below based on that stack overflow post and the process source (with using DWORD = System.UInt32; at the top).
internal static class NativeMethods
{
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
IntPtr lpPipeAttributes, int nSize);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle, SafeHandle hSourceHandle,
IntPtr hTargetProcess, out SafeFileHandle targetHandle, int dwDesiredAccess,
bool bInheritHandle, int dwOptions);
[StructLayout(LayoutKind.Sequential)]
public struct PIPE_SECURITY_ATTRIBUTES
{
public DWORD nLength;
public IntPtr lpSecurityDescriptor;
[MarshalAs(UnmanagedType.Bool)]
public bool bInheritHandle;
}
public static void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
{
PIPE_SECURITY_ATTRIBUTES lpPipeAttributes = new PIPE_SECURITY_ATTRIBUTES();
lpPipeAttributes.nLength = (DWORD)Marshal.SizeOf(lpPipeAttributes);
lpPipeAttributes.bInheritHandle = true;
lpPipeAttributes.lpSecurityDescriptor = IntPtr.Zero;
IntPtr attr = Marshal.AllocHGlobal(Marshal.SizeOf(lpPipeAttributes));
Marshal.StructureToPtr(lpPipeAttributes, attr, true);
SafeFileHandle hWritePipe = null;
try
{
if (parentInputs)
CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, attr, 0);
else
CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, attr, 0);
if (!DuplicateHandle(GetCurrentProcess(), hWritePipe, GetCurrentProcess(), out parentHandle, 0, false, 2))
throw new Exception();
}
finally
{
if ((hWritePipe != null) && !hWritePipe.IsInvalid)
{
hWritePipe.Close();
}
}
}
public static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe,
IntPtr lpPipeAttributes, int nSize)
{
hReadPipe = null;
if ((!CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid)
throw new Exception();
}
}

How do I set the monitor orientation in Windows 7?

I want to write some fun code to flip the orientation upside down on Windows 7. See screen shot of the option I want to control.
Here is the code I have:
class Program
{
public const long WM_PAINT=0x0F;
public const long WM_DISPLAYCHANGE=0x7E;
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
public struct DEVMODE // taken from Win API
{
...
public System.Windows.Forms.ScreenOrientation dmDisplayOrientation;
}
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
[DllImport("user32.dll", CharSet=CharSet.Ansi)]
public static extern int ChangeDisplaySettings(ref DEVMODE lpDevMode, int dwFlags);
[DllImport("User32.Dll")]
public static extern long PostMessage(IntPtr hWnd, long wMsg, long wParam, long lParam);
static void Main(string[] args)
{
ScreenOrientation ori=ScreenOrientation.Angle0;
DEVMODE mode=new DEVMODE()
{
dmSize=(short)Marshal.SizeOf(typeof(DEVMODE)),
dmDriverExtra=0,
dmDeviceName=new string(new char[32]),
dmFormName=new string(new char[32]),
};
try
{
EnumDisplaySettings(null, -1, ref mode);
if((mode.dmFields&0x80)>0)
{
ori=mode.dmDisplayOrientation;
}
mode.dmDisplayOrientation=ScreenOrientation.Angle270;
int temp=mode.dmPelsWidth;
mode.dmPelsWidth=mode.dmPelsHeight;
mode.dmPelsHeight=temp;
int ret=ChangeDisplaySettings(ref mode, 0);
PostMessage(Process.GetCurrentProcess().Handle, WM_DISPLAYCHANGE, 0, 0);
...
}
catch
{
}
}
}
which runs, but does not produce any affects.
Reference code: http://justlikeamagic.com/2009/05/21/changing-display-settings-programmatically/
and http://msdn.microsoft.com/en-us/library/ms812499.aspx#tbconchgscrn_chngingdisplay
On Windows 7, ChangeDisplaySetting has a known compatibility issue. The workaround is to call the WDK function: SetDisplayConfig.
http://social.msdn.microsoft.com/Forums/en/windowsuidevelopment/thread/5bc2396d-1e0e-44fb-b73b-95f8dfc45684
I have started something.
Please have a look: MultiMonitorHelper
It includes necessary structures for Win7, so that you could call SetDisplayConfig and other functions.
An actual example, how to rotate monitor 90 degrees:
int numPathArrayElements;
int numModeInfoArrayElements;
const QueryDisplayFlags pathType = QueryDisplayFlags.OnlyActivePaths;
// query active paths from the current computer.
// note that 0 is equal to SUCCESS!
// TODO; HARDCODE MAGIC VALUES AWAY.
if (CCDWrapper.GetDisplayConfigBufferSizes(pathType, out numPathArrayElements,
out numModeInfoArrayElements) == 0)
{
var pathInfoArray = new DisplayConfigPathInfo[numPathArrayElements];
var modeInfoArray = new DisplayConfigModeInfo[numModeInfoArrayElements];
// TODO; FALLBACK MECHANISM THAT HANDLES DIFFERENT VALUES FOR ZERO.
if (CCDWrapper.QueryDisplayConfig(
pathType,
ref numPathArrayElements, pathInfoArray,
ref numModeInfoArrayElements,
modeInfoArray, IntPtr.Zero) == 0)
{
pathInfoArray[0].targetInfo.rotation = DisplayConfigRotation.Rotate90;
CCDWrapper.SetDisplayConfig((uint) numPathArrayElements, pathInfoArray, (uint) numModeInfoArrayElements,
modeInfoArray, SdcFlags.Apply | SdcFlags.UseSuppliedDisplayConfig);
}
}
it's raw right now, meaning there is no "C# style" API right now, but none the less, you can use those structures.

Find handle of a ActiveX user control inside IE

How can I programatically find the handle of a user control in a webpage running on IE?
I'm able to find it using Spy++ but since the handle keeps changing I'm stuck.
I've been trying using FindWindow() but no luck :( I also wonder if I am doing something wrong or it simply only work for Windows...
Thanks in advance,
Zubrowka
I had a similar problem finding a PDF ActiveX Control inside a IE control in WPF.
To overcome the problem I used the EnumChildWindows API to find the correct child window and thus get its handle.
I'll include as much code as I can.
private static IntPtr FindPdfControlWindow(IntPtr parentHandle)
{
IntPtr result = IntPtr.Zero;
IntPtr matchPointer = IntPtr.Zero;
try
{
//allocate unmanaged memory for the result of the callback delegate
matchPointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
Marshal.WriteIntPtr(matchPointer, IntPtr.Zero)
//instantiate the delegate and pass to the API
NativeMethods.EnumWindowProc windowChecker = CheckForPdfControlWindow;
if (!NativeMethods.EnumChildWindows(parentHandle,
windowChecker,
matchPointer))
}
finally
{
if (matchPointer != IntPtr.Zero) Marshal.FreeHGlobal(matchPointer);
}
return result;
}
private static bool CheckForPdfControlWindow(IntPtr handle,
IntPtr matchPointer)
{
int captionLength = NativeMehtods.GetWindowTextLength(handle);
if (captionLength > 0)
{
StringBuilder buffer = new StringBuilder(captionLength + 1);
NativeMethods.GetWindowText(handle, buffer, buffer.Capacity);
if (buffer.ToString().Contains("Adobe"))
{
Marhsal.WriteIntPtr(matchPointer, handle)
return false;
}
}
return true;
}
private static class NativeMethods
{
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool EnumChildWindows(IntPtr window,
EnumWindowProc callback,
IntPtr i);
internal delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSer.Auto)]
internal static extern int GetWindowTextLength(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
internal static extern int GetWindowText(IntPtr hWnd,
StringBuilder lpString,
int nMaxCount);
}
transcribed in a rush so I hope it is both helpful and accurate.
If the ActiveX control is windowed, then you can query its IOleWindow interface to get the window handle.
Before you query interfaces from the ActiveX, you need to review the page's HTML to find a way to identify the activex in the document, such as element id.

Categories