Finding the hwnd of a textbox in another window - window

Im trying to get this thing to work all day.
Im trying to paste an input text to a textbox in another windows application, (lets say Chrome's URL bar)
I managed to get the main window hwnd, but when I used SendMessage, it just changed the title of the window
SO I am trying to figure out how exactly do I find out, dynamically, the hwnd of my textbox.
I also tried Spy++ but it gave me the same hwnd for all child windows for some reason, must did something wrong.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
[DllImport("User32.dll")]
public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
private static IntPtr WinGetHandle(string wName)
{
IntPtr hWnd = IntPtr.Zero;
foreach (Process pList in Process.GetProcesses())
{
if (pList.MainWindowTitle.Contains(wName))
{
hWnd = pList.MainWindowHandle;
}
}
return hWnd;
}
static void Main(string[] args)
{
List<IntPtr> myList = GetChildWindows(WinGetHandle("chrome"));
foreach(IntPtr ptr in myList)
{
//No idea which one of those IntPtr is actually my textbox
//SendMessage((IntPtr)788018, 0x000C, 0, "text here");
}
Console.ReadLine();
}
[DllImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool EnumChildWindows(IntPtr window, EnumWindowProc callback, IntPtr i);
public static List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}
private static bool EnumWindow(IntPtr handle, IntPtr pointer)
{
GCHandle gch = GCHandle.FromIntPtr(pointer);
List<IntPtr> list = gch.Target as List<IntPtr>;
if (list == null)
{
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
}
list.Add(handle);
// You can modify this to check to see if you want to cancel the operation, then return a null here
return true;
}
public delegate bool EnumWindowProc(IntPtr hWnd, IntPtr parameter);
}
}

I also tried Spy++ but it gave me the same hwnd for all child windows
for some reason, must did something wrong.
Not all applications use old-school "heavy" controls backed by a windows handle. For example, .Net WPF applications will exhibit the same behavior of just one handle for the main window itself. Everything is else drawn directly to the screen as needed via DirectX. Web browsers are "light" as well and don't use controls backed by a handles. Basically if Spy++ doesn't show child windows inside the main window then you won't be able to use the traditional window manipulation APIs or SendMessage().

Sadly I couldnt find a way to make it the way I wanted, but I found a workaround that works pretty well:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace ConsoleApplication1
{
class Program
{
[DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
static void Main(string[] args)
{
IntPtr hWnd = FindWindow(null, "Origin");
if (!hWnd.Equals(IntPtr.Zero))
{
// activate Notepad window
if (SetForegroundWindow(hWnd))
{
// send "Hello World!"
SendKeys.SendWait("Hello World!");
// send key "Tab"
SendKeys.SendWait("{TAB}");
// send key "Enter"
SendKeys.SendWait("{ENTER}");
}
}
}
}
}

Related

How to access *.lnk files, (shortcuts) from a UWP app

I would like to be able to access *.lnk files from any folder on my desktop.
I know that Windows.Storage cannot access *.lnk files but I can read and access them from from Win32 or System.IO in WPF apps.
Is there a way for me to incorporate FindFirstFileEx() into my app without getting access permission errors?
I have seen articles on FullTrustProcessLauncher Class and CustomCapabilities settings but no examples using Kernal32 methods, or advice on even if this would work.
but no examples using Kernal32 methods
For this scenario, you could use desktop-bridge desktop extension to approach, for detail steps please refer stfan's blog UWP with Desktop Extension.
For using Kernal32 methods, please refer pinvoke document.
public enum FINDEX_INFO_LEVELS
{
FindExInfoStandard = 0,
FindExInfoBasic = 1
}
public enum FINDEX_SEARCH_OPS
{
FindExSearchNameMatch = 0,
FindExSearchLimitToDirectories = 1,
FindExSearchLimitToDevices = 2
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr FindFirstFileEx(
string lpFileName,
FINDEX_INFO_LEVELS fInfoLevelId,
out WIN32_FIND_DATA lpFindFileData,
FINDEX_SEARCH_OPS fSearchOp,
IntPtr lpSearchFilter,
int dwAdditionalFlags);

How to call struct fields from c++ to c#?

Well, I am intended to apply Marshalling between managed and unmanaged codes. That is, to retrieve the result from a c++ code to c# code. I am not intended to know the definitions of managed or unmanaged here, rather am willing to find out the proper way of doing so.
Section: Unmanaged code
File: unmanaged_operation.cpp
#pragma managed(push, off)
typedef struct _IOperation
{
double stOutput;
double* dblPtrValue;
BSTR* parseValue;
BSTR* evenValue;
BSTR* oddValue;
BSTR* stripValue;
BSTR* contextValue;
BSTR* rangeValue;
} IOperation, *LPIOperation;
#pragma managed(pop)
#if (_MANAGED == 1) || (_M_CEE == 1)
#include <vcclr.h>
using namespace System;
using namespace System::Runtime::InteropServices;
#endif
// In unmanaged_operation.h file
extern __declspec(dllexport) LPIOperation operation;
// In unmanaged_operation.cpp file
__declspec(dllexport) LPIFactory factory = new IFactory();
extern "C" __declspec(dllexport) void __stdcall Parse(/* in */ BSTR* input)
{
BSTR* value = Do_Something_With_BSTR_Input_Value(input);
String^ _output = gcnew String(*value);
IntPtr ptr = Marshal::StringToBSTR(_output);
operation->parseValue = (BSTR*)ptr.ToPointer();
Marshal::FreeBSTR(ptr);
}
extern "C" __declspec(dllexport) void __stdcall Strip(/* in */ BSTR* input)
{
BSTR* value = Do_Something_With_BSTR_Input_Value(input);
String^ _output = gcnew String(*value);
IntPtr ptr = Marshal::StringToBSTR(_output);
operation->stripValue = (BSTR*)ptr.ToPointer();
Marshal::FreeBSTR(ptr);
}
extern "C" __declspec(dllexport) void __stdcall Range(/* in */ BSTR* input)
{
BSTR* value = Do_Something_With_BSTR_Input_Value(input);
String^ _output = gcnew String(*value);
IntPtr ptr = Marshal::StringToBSTR(_output);
operation->rangeValue = (BSTR*)ptr.ToPointer();
Marshal::FreeBSTR(ptr);
}
extern "C" __declspec(dllexport) void __stdcall Operate(/* in */ double input)
{
double output = Do_Something_With_Double_Input_Value(input);
operation->stOutput = output;
}
extern "C" __declspec(dllexport) LPIOperation GetOperation()
{
return operation;
}
Section: Managed code
File: managed_operation.cs
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct IOperation
{
/* MarshalAs(UnmanagedType.R8)] */
public double stOutput;
public double[] dblPtrValue;
/* MarshalAs(UnmanagedType.BStr)] */
public string parseValue;
};
Class Program
{
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void Parse([In] String input);
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void Strip([In] String input);
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void Range([In] String input);
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void Operate([In] Double input);
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern IntPtr GetOperation();
[STAThread]
public static void Main(string[] args)
{
try
{
IOperation operation = new IOperation();
Parse("The parse value.");
Strip("The strip value.");
Range("The range value.");
Operate((double)2.45);
IntPtr ptr = GetOperation();
// The following line throws the exception
operation = (IOperation)(Marshal.PtrToStructure(ptr, typeof(IOperation)));
// The above line throws the exception
Console.WriteLine("{0}", operation.parseValue);
Console.WriteLine("{0}", operation.stOutput);
}
catch (Exception e)
{
throw e;
// Exception of type 'System.ExecutionEngineException' was thrown.
}
}
}
Let's say that the Do_Something_With_BSTR_Input_Value method in unmanaged_operation.cpp be:
BSTR* __stdcall Do_Something_With_BSTR_Input_Value(/* in */ BSTR* input)
{
return input;
}
only for testing purpose, rather the original cited. And I wanted to print the same value to Console, that I passed as a parameter in Parse, Strip or Range method in managed_operation.cs
I used the following code for testing purpose in unmanaged_operation.cpp:
extern "C" __declspec(dllexport) void GetOperationPtr(LPIOperation output)
{
operation->stOutput = (double)2;
operation->parseValue = (BSTR*)"The BSTR string";
*output = *operation;
// OR output = operation;
}
And used the following code in managed_operation.cs
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct] out IntPtr ptr);
[STAThread]
public static void Main(string[] args)
{
try
{
IOperation operation = new IOperation();
Parse("The parse value.");
Strip("The strip value.");
Range("The range value.");
Operate((double)7.45);
IntPtr ptr;
GetOperationPtr(ptr);
// The following line throws the exception
operation = (IOperation)(Marshal.PtrToStructure(ptr, typeof(IOperation)));
// The above line throws the exception
Console.WriteLine("{0}", operation.parseValue);
Console.WriteLine("{0}", operation.stOutput);
}
catch (Exception e)
{
throw e;
// Cannot marshal 'parameter #1': Invalid managed/unmanaged type combination
// (Int/UInt must be paired with SysInt or SysUInt).
}
}
Again I changed the IntPtr to object in GetOperationPtr definition as follows:
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall)]
private static extern void GetOperationPtr([Out] [MarshalAs(UnmanagedType.Struct] out object ptr);
and in the Main method:
Object ptr;
GetOperationPtr(ptr);
which caused the application terminated instantly without being executed further.
Again when I omitted the MarshalAs attribute from the GetOperationPtr definition, the parseValue
returns garbage value something like 䕃洭獥慳敧 or 옧ﺧ㲨Ѹ㲨Ѹ or 멄攓�ѵ�ѵ rather any visible result.
To get rid of this I added the Charset parameter to DllImport attribute for GetOperation definition, as:
[DllImport(#"unmanaged_operation.dll"), CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
private static extern IntPtr GetOperation();
and used the same code in Main method as described earlier; which in such case shuffled the output value for each instance of IOperation field, as:
operation.parseValue returned "The strip value." for the method Parse("The parse value.");
operation.stripValue returned "The range value." for the method Strip("The strip value.");
operation.rangeValue returned "The parse value." for the method Range("The range value.");
Any suggestion with code example will highly be solicited.
Overall
It appears that your goal here is to call some C++ code from C#. In that case, it is generally better to write a C++/CLI managed class (keywords public ref class), and call the C++ code directly from there. You can access the managed class from C# in the normal way, just add a .Net reference and instantiate the class with C# new.
Specific issues
BSTR* value = Do_Something_With_BSTR_Input_Value(input);
String^ _output = gcnew String(*value);
IntPtr ptr = Marshal::StringToBSTR(_output);
operation->parseValue = (BSTR*)ptr.ToPointer();
Marshal::FreeBSTR(ptr);
If you're accessing a managed type (String^), it's not unmanaged code.
The way you're assigning the string is incorrect. You're allocating a new BSTR object in StringToBSTR, saving a pointer to it, and then freeing the BSTR object. You now have a pointer to deallocated/invalid memory! That memory can be re-used for anything at any time.
public struct IOperation
Don't start things with I unless they're an interface. That's the naming convention, stick with it.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct IOperation
{
/* MarshalAs(UnmanagedType.R8)] */
public double stOutput;
public double[] dblPtrValue;
/* MarshalAs(UnmanagedType.BStr)] */
public string parseValue;
};
If you're copying from unmanaged to managed, how big is the array of doubles? Which of the multitude of string representations is it supposed to use?
Recommendation
I recommend one of two things:
If you're interfacing to an unmanaged library which you are the author, consider changing that struct to be something easier to have the automatic marshaller deal with.
If you're not the author of the unmanaged library, or if you have other reasons for the struct to look like that, or if you just don't want to deal with the automatic marshaller, then do the marshalling manually: Write a C++/CLI managed class (public ref class) that takes the managed struct as a parameter, manually converts everything to the unmanaged struct, calls the unmanaged functions, and then copies everything back.

Impersonation is not working with windows 7

i am trying to impersonate another user before running a connecting to an API. my code has worked fine on windows xp, but 1 user received a windows 7 machine and the code no longer works. there is no error, imperonsation seems to be working, but no matter what I try the service is called using the current user's context. I have tried turning down UAC, still nothing. if I Run AS I can get the entire application to run as the other user, but this is not what I want to do. Anyone know what could cause impersonation to stop working on a machine?
CODE:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Principal;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Security.Permissions;
//This class is based on the code from here: http://msdn.microsoft.com/en- us/library/chf6fbt4.aspx
//And here: http://msdn.microsoft.com/en-us/library/system.security.principal.windowsidentity.aspx
public class Impersonator
{
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool LogonUser(String lpszUsername, String lpszDomain, String lpszPassword,
int dwLogonType, int dwLogonProvider, out SafeTokenHandle phToken);
private static WindowsImpersonationContext impersonationContext;
//For reference, not used yet.
//Ex: (int)Logontype.Interactive
enum LogonType
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}
//For reference, not used yet
enum LogonProvider
{
Default = 0,
WinNT35 = 1,
WinNT40 = 2,
}
// If you incorporate this code into a DLL, be sure to demand FullTrust.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public static void Impersonate(string user, string domain, string password)
{
try
{
//Impersonate a Windows User
SafeTokenHandle safeTokenHandle = default(SafeTokenHandle);
const int LOGON32_PROVIDER_DEFAULT = 0;
//This parameter causes LogonUser to create a primary token.
//const int LOGON32_LOGON_INTERACTIVE = 2; //Orig
const int LOGON32_LOGON_INTERACTIVE = 2; //2,9,7
// Call LogonUser to obtain a handle to an access token.
bool returnValue = LogonUser(user, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out safeTokenHandle);
WindowsIdentity windowsIdentity = new WindowsIdentity (safeTokenHandle.DangerousGetHandle());
impersonationContext = windowsIdentity.Impersonate();
//WindowsIdentity.Impersonate(safeTokenHandle.DangerousGetHandle());
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public static void StopImpersonating()
{
try
{
// Stop impersonating the user.
impersonationContext.Undo();
// Check the identity name.
Console.Write("Name of the identity after performing an Undo on the");
Console.WriteLine(" impersonation: " + WindowsIdentity.GetCurrent().Name);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32.SafeHandles;
using System.Runtime.InteropServices;
using System.Security;
using System.Runtime.ConstrainedExecution;
public sealed class SafeTokenHandle : SafeHandleZeroOrMinusOneIsInvalid
{
private SafeTokenHandle()
: base(true)
{
}
[DllImport("kernel32.dll")]
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[SuppressUnmanagedCodeSecurity]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CloseHandle(IntPtr handle);
protected override bool ReleaseHandle()
{
return CloseHandle(handle);
}
}

Hooking NtCreateFile API from ntdll.dll with EasyHook (c#)

This is the first time I try to hook windows API. My goal is to monitor all files that a process is going to create/open/read/write.
In order to be the most verbose possible, I decided to hook the ntdll.dll API such as NtCreateFile() and NtOpenFile(). So, in order to acheive this goal, I went on EasyHook, which seems easy and robust.
I've essetially followed the FileMon example, changing what I really wanted: the Hooked function.
When I try to read information about the file that is going to be opened, I try to read information from the OBJECT_ATTRIBUTES structure, such as
the ObjectName. Those are integer pointers, so I expected to use the function Marshal.PtrToStringAuto(attributes.objectName) in order to get the string value. However, the result is I can only have bad strings, without any meaning. Also, the File access seems to be not working. I guess there's something wrong with this
code, maybe in the DllImport signatures. Be adviced I had to replace SafeHandle with IntPtr, because of EasyHook was complaining about marshaling them.
Can someone help me?
Here's my specific code of the injected DLL:
Here's the Run method code
public void Run(RemoteHooking.IContext InContext, String inChannelName)
{
// First of all, install all the hooks
try
{
// NtCreateFile
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtCreateFile"),
new CreateFileDelegate(CreateFile_Hooked),
this
);
fileCreationHook = LocalHook.Create(
LocalHook.GetProcAddress("ntdll.dll", "NtOpenFile"),
new OpenFileDelegate(OpenFile_Hooked),
this
);
fileCreationHook.ThreadACL.SetExclusiveACL(new Int32[] { 0 });
remoteIf.Log("File creation Hook correctly installed on pid "+RemoteHooking.GetCurrentProcessId());
}
catch (Exception e)
{
remoteIf.Log(e.Message);
remoteIf.Log(e.StackTrace);
return;
}
// Wake up the process
remoteIf.Log("Waiking up process...");
RemoteHooking.WakeUpProcess();
while (true)
{
Thread.Sleep(500);
if (queue.Count > 0)
{
String[] package = null;
lock (queue)
{
package = queue.ToArray();
queue.Clear();
}
remoteIf.OnCreateFile(RemoteHooking.GetCurrentProcessId(), package);
}
else
remoteIf.Ping();
}
}
Here's the contructor code:
public InjectedDLL(RemoteHooking.IContext InContext, String inChannelName)
{
// Create the structure which will contain all the messages
queue = new Stack<string>();
// Initiate the connection to the Injector process, getting back its interface
remoteIf = RemoteHooking.IpcConnectClient<IPCInterface>(inChannelName);
// Try invocating a method to test the connection.
remoteIf.Ping();
}
Here there are the Hook delegate and the hook function
public delegate int CreateFileDelegate(out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
public int CreateFile_Hooked(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength)
{
//string s = Marshal.PtrToStringAuto(objectAttributes.ObjectName);
int res = NtCreateFile(out handle, access,ref objectAttributes,out ioStatus, ref allocSize,fileAttributes, share,createDisposition,createOptions,eaBuffer,eaLength);
return res;
}
Here there are the NtDll.Dll native functions:
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtCreateFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
ref long allocSize,
uint fileAttributes,
System.IO.FileShare share,
uint createDisposition,
uint createOptions,
IntPtr eaBuffer,
uint eaLength);
[DllImport("ntdll.dll", ExactSpelling = true, SetLastError = true)]
public static extern int NtOpenFile(
out IntPtr handle,
System.IO.FileAccess access,
ref OBJECT_ATTRIBUTES objectAttributes,
out IO_STATUS_BLOCK ioStatus,
System.IO.FileShare share,
uint openOptions
);
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct OBJECT_ATTRIBUTES
{
public Int32 Length;
public IntPtr RootDirectory;
public IntPtr ObjectName;
public uint Attributes;
public IntPtr SecurityDescriptor;
public IntPtr SecurityQualityOfService;
}
[StructLayout(LayoutKind.Sequential, Pack = 0)]
public struct IO_STATUS_BLOCK
{
public uint status;
public IntPtr information;
}
ObjectName is a pointer to a UNICODE_STRING struct. A managed equivalent looks like this:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct UNICODE_STRING
{
public ushort Length;
public ushort MaximumLength;
[MarshalAs(UnmanagedType.LPWStr)]
public String Buffer;
}
You need to use marshalling to get a managed copy of the struct.
var us = Marshal.PtrToStructure<UNICODE_STRING>(objectAttributes.ObjectName);
Once you have the managed struct you can access the Buffer field to get the name of the object.

get all handles

How can I get all handles of a window via Win32? I found this code but I don't know how is it work!
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
private List<IntPtr> GetChildWindows(IntPtr parent)
{
List<IntPtr> result = new List<IntPtr>();
GCHandle listHandle = GCHandle.Alloc(result);
try
{
EnumWindowProc childProc = new EnumWindowProc(EnumWindow);
EnumChildWindows(parent, childProc, GCHandle.ToIntPtr(listHandle));
}
finally
{
if (listHandle.IsAllocated)
listHandle.Free();
}
return result;
}

Resources