Is processes threads (or whole process) suspended - c#

I would like to check if the processes' threads (the whole process) are suspended.
I'm obtaining each process thread by this code:
var threads = Proc.Threads;
for (int x = 0; x < threads.Count; x++) {
var thread = threads[x];
However System.Diagnostics.ThreadState doesn't contain Suspended, but System.Threading.ThreadState does. How do I convert System.Diagnostics.ThreadState to System.Threading.ThreadState, or is it some other method to check it? I'm not trying to suspend/resume them, just I want to know how Process hacker/Process explorer does that.

Microsoft made a big mistake in .NET version 1.0, they added the Thread.Suspend() and Resume() methods. Those methods were widely abused, programmers used them to implement thread synchronization. For which they are entirely inappropriate. Problem was that it usually worked. But call Suspend() at an unlucky time and you'll freeze a thread while it is buried inside a Windows call, holding a global lock. And causing the entire program to deadlock.
It was not the only design mistake they made, the Synchronized method on the collection classes was quite a disaster as well. Widely misinterpreted as "returns a thread-safe collection".
Live and learn, this all got fixed in .NET 2.0. One big overhaul was that a Thread may not necessarily be an operating system thread anymore, that never actually got implemented. But explains why there are two ThreadState enumerations, one for Thread (the .NET version) and another for ProcessThread (the operating system version). And they closed the loophole on programmers abusing Suspend/Resume, the methods were declared obsolete. And they closed the backdoor as well, you can't find out from ProcessThread that a thread is suspended.
Feature, not a bug. Don't make the same mistake, knowing that a thread is suspended is useless knowledge, it may well not be suspended anymore a microsecond later.

This will help someone.
Process proc = Process.GetProcessById(31448);
if(proc.Threads[0].WaitReason == ThreadWaitReason.Suspended)
{
//process is suspended
}

An operating system thread isn't the same as a .Net thread. Process.Threads returns OS threads, each of which may or may not correspond to a .Net thread.
You can look at ProcessThread.WaitReason, but it doesn't correspond to .Net wait states

You could improperly use SuspendThread or Wow64SuspendThread to find out if it was suspended, then use ResumeThread to restore the situation.
SuspendThread return: "If the function succeeds, the return value is the thread's previous suspend count;"
Declarations:
[Flags] public enum ThreadAccess : int {
TERMINATE = (0x0001),
SUSPEND_RESUME = (0x0002),
GET_CONTEXT = (0x0008),
SET_CONTEXT = (0x0010),
SET_INFORMATION = (0x0020),
QUERY_INFORMATION = (0x0040),
SET_THREAD_TOKEN = (0x0080),
IMPERSONATE = (0x0100),
DIRECT_IMPERSONATION = (0x0200)}
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(
ThreadAccess dwDesiredAccess,
bool bInheritHandle,
uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
[DllImport("kernel32.dll")]
static extern int ResumeThread(IntPtr hThread);
[DllImport("kernel32", CharSet = CharSet.Auto, SetLastError = true)]
static extern bool CloseHandle(IntPtr handle);
(Wow64SuspendThread link hidden because i need 10 reputation to put over 2 links = ht.tps://msdn.microsoft.com/it-it/library/windows/desktop/ms687400(v=vs.85).aspx)

Related

stackoverflow with dllimport from IIS

Scenario
I made a Wrapper class for a C dll so I can call its functions from managed code and I can access them from a c# WCF Service. Everyting seems fine, but when allocating a lot of memory in the C library. IIS does not seems to like it. It will give me a stackoverflow exception.
Question
When allocating memory in the C dll. It breaks in IIS.
char stmt[163840+1]; // allocation a lot of memory
char stmt2[163840+1]; // allocation a lot of memory
Does IIS have special setting to allow more memory to be allocated from the C module?
Code which expose C dll functions
Steps:
1. use SetDllDirectory
2. LoadLibrary
3. then call my function with DLLImport
4. FreeLibrary
The NativeClassWrapper code (Simplefied)
[SuppressUnmanagedCodeSecurity]
public static class NativeClassWrapper
{
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr LoadLibrary(string hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
[DllImport("myCDll.dll", EntryPoint = "MyFunction", ExactSpelling = false, SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
public static extern int MyFunction(
);
}
C code
long MyFunction () {
char stmt[163840+1]; // allocation alot of memory
char stmt2[163840+1];
long lReturn = 0L;
*stmt = '\0';
*stmt2 = '\0';
return lReturn;
}
char stmt[163840+1]; // allocation alot of memory
char stmt2[163840+1];
These allocations are responsible for the stack overflow. You are attempting to allocate large arrays on the stack, and the stack is not large enough. The default stack for a Windows application is 1MB. The arrays on their own will not overflow such a stack. However, it's quite plausible that IIS uses smaller stacks, or that there is not code that you have not shown that makes similar large stack allocations.
If you really need to allocate such large arrays, you'll should do so on the heap.
thanks David Heffernan for youre response. I accpet his answer because it helped me to this solution. The solution I choose was to Start the process that communicates with the C Dll on a different thread and allocate the stacksize to 1MB instead of the default 256KB
public void StartNewThread()
{
const int stacksize = 1024*1024; // 1MB
var thread = new Thread(NativeDllProcess, stacksize);
thread.Start();
thread.Join(); // Wait till thread is ready
// .. rest of code here
}
private void NativeDllProcess(object info)
{
// ..... Code thats calls C dll functions
}
More information here :
maximum / default stack size IIS
By default, the maximum stack size of a thread that is created in a native IIS process is 256 KB
 Email
 Print
SUMMARY
By default, the maximum stack size of a thread that is created by a native Microsoft Internet Information Services (IIS) process is 256 KB prior to Windows Server 2008. For example, when Inetinfo.exe, DLLHost.exe, or W3wp.exe creates a thread in IIS 5.0 or IIS 6.0, the maximum stack size of the thread is 256 KB by default. You can also explicitly call the CreateThread function to specify the stack size of the thread. In Microsoft Windows 2000, if the Microsoft ASP.NET Worker Process (ASPNet_wp.exe) creates a thread, the maximum stack size of the thread is 1 MB. In Windows Server 2008 and higher, the maximum stack size of a thread running on 32-bit version of IIS is 256 KB, and on an x64 server is 512 KB.
NOTE: Internet Information Services is a multi-threaded web application platform that allows application code running inside of each worker process to utilize hundreds or more threads at once as necessary. Each thread is bound by the same stack size limit in order to keep the virtual memory usage of the process within manageable limits.

Get StartAddress of win32 thread from another process

Background:
I've written a multi-threaded application in Win32, which I start from C# code using Process class from System.Diagnostics namespace.
Now, in the C# code, I want to get the name/symbol of the start address of each thread created in the Win32 application so that I could log thread related information, such as CPU usage, to database. Basically, C# code starts multiple instances of the Win32 Application, monitors them, kills if needed, and then logs info/error/exceptions/reason/etc to database.
For this purpose, I've wrapped two Win32 API viz. SymInitialize and SymFromAddr in programmer-friendly API written by myself, as listed below:
extern "C"
{
//wraps SymInitialize
DllExport bool initialize_handler(HANDLE hModue);
//wraps SymFromAddr
DllExport bool get_function_symbol(HANDLE hModule, //in
void *address, //in
char *name); //out
}
And then call these API from C# code, using pinvoke. But it does not work and GetLastError gives 126 error code which means:
The specified module could not be found
I'm passing Process.Handle as hModule to both functions; initialize_handler seems to work, but get_function_symbol does not; it gives the above error. I'm not sure if I'm passing the correct handle. I tried passing the following handles:
Process.MainWindowHandle
Process.MainModule.BaseAddress
Both fail at the first step itself (i.e when calling initialize_handler). I'm passing Process.Threads[i].StartAddress as second argument, and that seems to be cause of the failure as ProcessThread.StartAddress seems to be the address of RtlUserThreadStart function, not the address of the start function specific to the application. The MSDN says about it:
Every Windows thread actually begins execution in a system-supplied function, not the application-supplied function. The starting address for the primary thread is, therefore, the same (as it represents the address of the system-supplied function) for every Windows process in the system. However, the StartAddress property allows you to get the starting function address that is specific to your application.
But it doesn't say how to get the startinbg function address specific to the application, using ProcessThread.StartAddress.
Question:
My problem boils to getting the start address of win32 thread from another application (written in C#), as once I get it, I will get the name as well, using the above mentioned APIs. So how to get the start address?
I tested my symbol lookup API from C++ code. It works fine to resolve the address to a symbol, if given the correct address to start with.
Here is my p/invoke declarations:
[DllImport("UnmanagedSymbols.dll", SetLastError = true, CallingConvention= CallingConvention.Cdecl)]
static extern bool initialize_handler(IntPtr hModule);
[DllImport("UnmanagedSymbols.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
static extern bool get_function_symbol(IntPtr hModule, IntPtr address, StringBuilder name);
The key is to call the NtQueryInformationThread function. This is not a completely "official" function (possibly undocumented in the past?), but the documentation suggests no alternative for getting the start address of a thread.
I've wrapped it up into a .NET-friendly call that takes a thread ID and returns the start address as IntPtr. This code has been tested in x86 and x64 mode, and in the latter it was tested on both a 32-bit and a 64-bit target process.
One thing I did not test was running this with low privileges; I would expect that this code requires the caller to have the SeDebugPrivilege.
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
class Program
{
static void Main(string[] args)
{
PrintProcessThreads(Process.GetCurrentProcess().Id);
PrintProcessThreads(4156); // some other random process on my system
Console.WriteLine("Press Enter to exit.");
Console.ReadLine();
}
static void PrintProcessThreads(int processId)
{
Console.WriteLine(string.Format("Process Id: {0:X4}", processId));
var threads = Process.GetProcessById(processId).Threads.OfType<ProcessThread>();
foreach (var pt in threads)
Console.WriteLine(" Thread Id: {0:X4}, Start Address: {1:X16}",
pt.Id, (ulong) GetThreadStartAddress(pt.Id));
}
static IntPtr GetThreadStartAddress(int threadId)
{
var hThread = OpenThread(ThreadAccess.QueryInformation, false, threadId);
if (hThread == IntPtr.Zero)
throw new Win32Exception();
var buf = Marshal.AllocHGlobal(IntPtr.Size);
try
{
var result = NtQueryInformationThread(hThread,
ThreadInfoClass.ThreadQuerySetWin32StartAddress,
buf, IntPtr.Size, IntPtr.Zero);
if (result != 0)
throw new Win32Exception(string.Format("NtQueryInformationThread failed; NTSTATUS = {0:X8}", result));
return Marshal.ReadIntPtr(buf);
}
finally
{
CloseHandle(hThread);
Marshal.FreeHGlobal(buf);
}
}
[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtQueryInformationThread(
IntPtr threadHandle,
ThreadInfoClass threadInformationClass,
IntPtr threadInformation,
int threadInformationLength,
IntPtr returnLengthPtr);
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, int dwThreadId);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool CloseHandle(IntPtr hObject);
[Flags]
public enum ThreadAccess : int
{
Terminate = 0x0001,
SuspendResume = 0x0002,
GetContext = 0x0008,
SetContext = 0x0010,
SetInformation = 0x0020,
QueryInformation = 0x0040,
SetThreadToken = 0x0080,
Impersonate = 0x0100,
DirectImpersonation = 0x0200
}
public enum ThreadInfoClass : int
{
ThreadQuerySetWin32StartAddress = 9
}
}
Output on my system:
Process Id: 2168 (this is a 64-bit process)
Thread Id: 1C80, Start Address: 0000000001090000
Thread Id: 210C, Start Address: 000007FEEE8806D4
Thread Id: 24BC, Start Address: 000007FEEE80A74C
Thread Id: 12F4, Start Address: 0000000076D2AEC0
Process Id: 103C (this is a 32-bit process)
Thread Id: 2510, Start Address: 0000000000FEA253
Thread Id: 0A0C, Start Address: 0000000076F341F3
Thread Id: 2438, Start Address: 0000000076F36679
Thread Id: 2514, Start Address: 0000000000F96CFD
Thread Id: 2694, Start Address: 00000000025CCCE6
apart from the stuff in parentheses since that requires extra P/Invoke's.
Regarding SymFromAddress "module not found" error, I just wanted to mention that one needs to call SymInitialize with fInvadeProcess = true OR load the module manually, as documented on MSDN.
I know you say this isn't the case in your situation, but I'll leave this in for the benefit of anyone who finds this question via those keywords.
Here's what my understanding of the problem is.
You have a C# app, APP1 that creates a bunch of threads.
Those threads, in turn, each create a process. I am assuming those threads stay alive and are in charge of monitoring the process it spawned.
So for each thread in APP1, you want it to enumerate information on the threads spawned in the child process of that thread.
They way I would have done this back in the good-old-days would be:
Code all my Win32 thread monitoring of a given Win32 process into a DLL
Inject that DLL into the process I wanted to monitor
Use a named pipe or other RPC mechanism to communicate from the injected Win32 process to the host APP1
So in your main threadproc in C#, you would create and monitor a named pipe for your process to communicate once it has been injected.
In C++ land, the pseudo code would be to then create a suspended process, allocate some memory in that process, inject your DLL into the process, then create a remote thread that would execute your injected dll:
char * dllName = "your cool dll with thread monitoring stuff.dll"
// Create a suspended process
CreateProces("your Win32 process.exe", ...CREATE_SUSPENDED..., pi)
// Allocate memory in the process to hold your DLL name to load
lpAlloc = VirtualAlloc(ph.hProcess, ... MEM_COMMIT, PAGE_READWRITE)
// Write the name of your dll to load in the process memory
WriteProcessMemeory(pi.hProcess, lpAlloc, dllName, ...)
// Get the address of LoadLibrary
fnLoadLibrary = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA")
// Create a remote thread in the process, giving it the threadproc for LoadLibrary
// and the argument of your DLL name
hTrhead = CreateRemoteThread(pi.hProcess, ..., fnLoadLibrary, lpAlloc, ...)
// Wait for your dll to load
WaitForSingleObject(hThread)
// Go ahead and start the Win32 process
ResumeThread(ph.hThread)
In your DLL, you could put code into DLL_PROCESS_ATTACH that would connect to the named pipe you set up, and initialize all your stuff. Then fire a function to begin monitoring and reporting on the named pipe.
Your C# threadproc would monitor the named pipe for its process, and report it on up to APP1.
UPDATE:
I missed the fact that you control the code for the Win32 proccess. In that case, I would just pass an argument to the proccess that would control the RPC mechanism of your choice for communication (Shared memory, named pipes, queue service, clipboard (ha), etc).
That way, your C# threadproc sets up the RPC communication channel and monitoring, and then provides the "address" information to your Win32 process so it can "dial you back".
I'll leave the other stuff up there in case it is useful to anyone else wanting to monitor a Win32 process where they are not in charge of the code.
Well, this is definitely not the straightforward approach, but maybe it will help you somehow. You should be able to get the stack trace of another thread in a way used by this project (StackWalk64) and eventually see the name of desired function. It has its own problems, particularly performance of this approach probably won't be too high, but as I understood this is one-shot per thread operation. Question is, will it generally be able to properly walk the stack of your (probably optimized) applications.
First, you can't really do this reliably: if you happen to access Thread.StartAddress before the thread executes the function pointer or after the function returns, you will have no way to know what the starting function actually is.
Secondly, the more likely answer is that there isn't a direct mapping to the starting function when the thread starting function is managed.

C# : How to detect if screen reader is running?

How to detect if screen reader is running (JAWS)?
As I understand in .NET 4 we can use AutomationInteropProvider.ClientsAreListening from System.Windows.Automation.Provider namespace, but what if I have to do it for .NET 2.0?
I tried to inspect ClientsAreListening source code, it calls external RawUiaClientsAreListening method from UIAutomationCore.dll library.
Do you have any ideas how to implement JAWS detection in .NET 2.0?
Use the SystemParametersInfo function passing a uiAction of SPI_GETSCREENREADER.
You will need to use P/Invoke for this, for example:
internal class UnsafeNativeMethods
{
public const uint SPI_GETSCREENREADER = 0x0046;
[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SystemParametersInfo(uint uiAction, uint uiParam, ref bool pvParam, uint fWinIni);
}
public static class ScreenReader
{
public static bool IsRunning
{
get
{
bool returnValue = false;
if (!UnsafeNativeMethods.SystemParametersInfo(UnsafeNativeMethods.SPI_GETSCREENREADER, 0, ref returnValue, 0))
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "error calling SystemParametersInfo");
}
return returnValue;
}
}
}
This is possibly better than using the ClientsAreListening property as this property appears to return true for any automation client, not just screen readers.
Also see:
Using SystemParametersInfo from C# (SPI_GETSCREENREADER SPI_SETSCREENREADER) (Stack Overflow)
INFO: How Clients and Servers Should Use SPI_SETSCREENREADER and SPI_GETSCREENREADER (Microsoft KB)
You should also listen for the WM_SETTINGCHANGE message to detect if a screen reader starts / stops running.
Update (in response to BrendanMcK's comments):
Although this is never explicitly documented in as many words, looking at the description of the flag I think the purpose of this flag is relatively clear:
Determines whether a screen reviewer utility is running. A screen reviewer utility directs textual information to an output device, such as a speech synthesizer or Braille display. When this flag is set, an application should provide textual information in situations where it would otherwise present the information graphically.
What this is saying is that applications set this flag whenever an application wishes the UI to behave as if a screen reader is running, regardless of whether or not that application is actually a screen reader or not.
Suitable things to do in response to this flag is to add text in order to "read" otherwise intuitive UI state to the user. If radical changes are needed to make your UI screen reader accessible then the chances are that your UI also isn't that intuitive to sigted users and could probably do with a re-think.

Pausing all threads in current process at run time

I have a bug in my app that seems to show it's face only when I pause the app in the debugger for a few minutes. I suspect this is due to a third party networking library I am using having a heartbeat thread, which becomes disconnected when it can not ping the server while it's heartbeat thread is paused.
I am trying to write a test case app for this to verify that this is the cause of the bug. To do so, I need a way to pause all the threads in the app (which i will later narrow down to pausing only the thread I suspect may be the heartbeat thread) to simulate pausing the app in the debugger.
Does anyone know how to do this? Is it even possible for one thread to cause another to sleep?
Thanks,
Alex
UPDATE:
I ended up deciding that I didn't really need an app to do this for me, seeing as the point was just to verify that pausing in the debugger was causing the disconnect. So, here's what I did... (The simplest ways are often the best... or at least the simplest...)
private static void Main(string[] args)
{
IPubSubAdapter adapter = BuildAdapter();
bool waitingForMessage;
adapter.Subscribe(_topic, message => waitingForMessage = false, DestinationType.Topic);
Stopwatch timePaused = new Stopwatch();
while (adapter.IsConnected)
{
Console.WriteLine("Adapter is still connected");
waitingForMessage = true;
adapter.Publish(_topic, "testmessage", DestinationType.Topic);
while (waitingForMessage)
{
Thread.Sleep(100);
}
timePaused.Reset();
timePaused.Start();
Debugger.Break();
timePaused.Stop();
Console.WriteLine("Paused for " + timePaused.ElapsedMilliseconds + "ms.");
Thread.Sleep(5000); // Give it a chance to realise it's disconnected.
}
Console.WriteLine("Adapter is disconnected!");
Console.ReadLine();
}
And the output:
Adapter is still connected
Paused for 10725ms.
Adapter is still connected
Paused for 13298ms.
Adapter is still connected
Paused for 32005ms.
Adapter is still connected
Paused for 59268ms.
Adapter is disconnected!
You can use this to quickly indentify the threads of your process:
using System.Diagnostics;
ProcessThreadCollection threads = Process.GetCurrentProcess().Threads;
Then you can use kernel32.dll with P/Invoke to do whatever you need with those threads. Use OpenThread to get a handle to the desired thread and then suspend it with SuspendThread using that handle.
Here are the P/Invoke declaration for the two methods:
[DllImport("kernel32.dll")]
static extern IntPtr OpenThread(uint dwDesiredAccess, bool bInheritHandle, uint dwThreadId);
[DllImport("kernel32.dll")]
static extern uint SuspendThread(IntPtr hThread);
I assume you're not going to pause all the threads in the application, otherwise there will be nothing running to un-suspend them. Or have I missed something?
A suggestion: Try giving names to all the threads you create. Any threads without a name, or that don't match your naming convention, must have been create by a third-party component. This might get you to the root cause quicker without having to pause lots of threads.
From my pov this sounds not right.
Which kind of tests are you writing? If you are speaking about unit tests, then this is not a Unit test case - sounds more like integration test
Consider to isolate the API calls in a class and then use dependendcy injection so that you can test without the 3rd party library with mocks/stubs and can also provoke/test an exception raised from the 3rd party library
You can suspend threads by calling Thread.Suspend. The documentation gives big "DON'T DO THIS!" deprecation warnings, but I think yours is a valid use case.
Jon Skeet thinks you can't enumerate managed threads in normal C# code, though he does hint at a possible solution.
You could call the Thread.Suspend then Thread.Resume methods but those are deprecated and is not recommneded to use them.
But you can do the following:
Have a boolean flag that when set you put your thread on a big sleep.
OR
Better to use ManualResetEvent.

Issue with callback method in SetTimer Windows API called from C# code

I'm currently involved in a project that is migrating some old VB6 code to C# (.Net Framework 3.5). My mandate is to just do the migration; any functional enhancements or refactoring is to be pushed to a later phase of the project. Not ideal, but there you go.
So part of the VB6 code makes a call out to the Windows API SetTimer function. I've migrated this and cannot get it to work.
The migrated project builds as a DLL; I've created a small WinForms test harness that links to the DLL and calls the code in question. Very simple, just to prove that the call can be made.
The relevant code in the migrated DLL is as follows:
[DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
public extern static int SetTimer(int hwnd, int nIDEvent, int uElapse, AsyncObjectCallerDelegate lpTimerFunc);
public delegate void AsyncObjectCallerDelegate(int hwnd, int uMsg, int idEvent, int dwTime);
static public int StartTimer( AsyncGeoServer.GeoWrapper AsyncObj)
{
m_objGeoWrapper = AsyncObj;
int lngReturn = SetTimer(0, 0, 1, new AsyncObjectCallerDelegate(AsyncObjectCaller));
// When the line below is removed, the call functions correctly.
// MessageBox.Show("This is a temp message box!", "Temp Msg Box", MessageBoxButtons.OKCancel);
return lngReturn;
}
static private void AsyncObjectCaller(int hwnd, int uMsg, int idEvent, int dwTime)
{
// Perform processing here - details removed for clarity
}
static public void StopTimer( int TimerID)
{
try { KillTimer(0, TimerID); }
catch { }
}
The above calls are wrapped by the DLL in an outer DoProcessing() method; this creates an event using CreateEvent before calling StartTimer (both Windows Kernel calls), then calls WaitForSingleObject before continuing processing. The AsyncObjectCaller function will set the event as part of its execution to allow processing to continue.
So my issue is this: if the code is called as listed above, it fails. The AsyncObjectCaller callback method never gets triggered and the WaitForSingleObject call times out.
If, however, I uncomment the MessageBox.Show call in StartTimer, it works as expected... sort of. The AsyncObjectCaller callback method gets triggered immediately after the call to MessageBox.Show. I've tried putting MessageBox.Show in various locations in the code, and it's the same no matter where I put it (as long as it's called after the call to SetTimer) - the callback function doesn't get triggered until the messagebox is displayed.
I'm completely stumped, and none too familiar with either VB6 or Windows API coding, coming from a mainly .Net background.
Thanks for any help!
Your AsyncObjectCallerDelegate is incorrect. It might work in 32-bit code, but will fail miserably in 64-bit. The Windows API function prototype is:
VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
In C#, that would be:
delegate void AsyncObjectCallerDelegate(IntPtr hWnd, uint uMsg, IntPtr nIDEvent, uint dwTime);
Also, your managed prototype should be:
static extern IntPtr SetTimer(IntPtr hWnd, IntPtr nIDEvent, uint uElapse, AsyncObjectCallerDelegate lpTimerFunc);
That said, I'd echo what Alex Farber said: you should use one of the .NET timer objects for this. Since this doesn't appear to be a UI timer (you're passing 0 for the window handle), I'd suggest System.Timers.Timer or System.Threading.Timer. If you want the timer to raise an event, use System.Timers.Timer. If you want the timer to call a callback function, use System.Threading.Timer.
Note that the event or callback will be executed on a pool thread--NOT the program's main thread. So if the processing will be accessing any shared data, you'll have to keep thread synchronization issues in mind.
The problem is that your program is not pumping a message loop or is not letting the UI thread go idle. An API function like SetTimer() requires a message loop to work. Application.Run() in a Windows Forms project for example. The callback can only run when the your main thread is inside the loop, dispatching Windows messages.
It works when you use MessageBox.Show(), that's a function that pumps its own message loop. So that the message box can respond to the user clicking the OK button. But of course, that will only work for as long as the box is up.
You'll probably need to restructure your program so it is based on a Windows Forms project template. Calling Application.DoEvents() in a loop is a very imperfect workaround.
public extern static int SetTimer(int hwnd, int nIDEvent, int uElapse, IntPtr lpTimerFunc);
int lngReturn = SetTimer(0, 0, 1, Marshal.GetFunctionPointerForDelegate(new AsyncObjectCallerDelegate(AsyncObjectCaller)));
I understand that your mandate is to just do the migration, but i any case, it is better to use Windows Forms timer instead of this, it wraps native SetTimer API, and there is no need in these interoperability tricks.

Categories

Resources