使用C#调用c语言的dll,如果dll运行很长时间,有办法在c#代码中强制停止dll的运行吗?我试了thread.abort只是把调用者退出了,dll还在运行。
C DLL源代码
CalcLib.h
#define RETI __declspec(dllexport)
RETI void SetA(int a);
RETI void SetB(int b);
RETI int Add();
CalcLib.c
int va = 0;
int vb = 0;
RETI void SetA(int a)
{
va = a;
}
RETI void SetB(int b)
{
vb = b;
}
RETI int Add()
{
Sleep(5000);
printf("Add Result\\n");
return va + vb;
}
C#程序
NativeMethod.cs
public static class NativeMethod
{
[DllImport("kernel32.dll", EntryPoint = "LoadLibrary")]
public static extern int LoadLibrary(
[MarshalAs(UnmanagedType.LPStr)] string lpLibFileName);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress")]
public static extern IntPtr GetProcAddress(int hModule,
[MarshalAs(UnmanagedType.LPStr)] string lpProcName);
[DllImport("kernel32.dll", EntryPoint = "FreeLibrary")]
public static extern bool FreeLibrary(int hModule);
}
InteropLib.cs
public class InteropLib
{
private string _dllName;
int _hModule = 0;
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate int DoAction();
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
private delegate void SetVariable(int va);
private DoAction _doAction;
private Dictionary<string, SetVariable> _setVariables = new Dictionary<string,SetVariable>();
public InteropLib(string dll)
{
_dllName = dll;
_hModule = NativeMethod.LoadLibrary(_dllName);
}
public int AddSettings(string name,string function)
{
IntPtr intPtr = NativeMethod.GetProcAddress(_hModule, function);
SetVariable setFuntion = (SetVariable)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(SetVariable));
_setVariables.Add(name,setFuntion);
return 0;
}
public int setDoAction(string function)
{
IntPtr intPtr = NativeMethod.GetProcAddress(_hModule, function);
_doAction = (DoAction)Marshal.GetDelegateForFunctionPointer(intPtr, typeof(DoAction));
return 0;
}
public void SetFunction(string key,int value)
{
_setVariables[key](value);
}
public int Action()
{
return _doAction();
}
}
测试程序
class MainClass
{
static void Main(string[] args)
{
int ret = 111;
ret = CallWithTimeout2(FiveSecondMethodRet, 6000);
Console.WriteLine(ret);
ret = CallWithTimeout2(FiveSecondMethodRet, 4000);
Console.WriteLine(ret);
ret = CallWithTimeout2(DllTest, 7000);
Console.WriteLine(ret);
ret = CallWithTimeout2(DllTest, 1000);
Console.WriteLine(ret);
Console.Read();
}
static int FiveSecondMethodRet()
{
Thread.Sleep(5000);
Console.WriteLine("FiveSecond");
return 0;
}
static int DllTest()
{
var il = new InteropLib("CalcLib.dll");
il.AddSettings("a", "SetA");
il.AddSettings("b", "SetB");
il.setDoAction("Add");
il.SetFunction("a", 7);
il.SetFunction("b", 2);
int ret = il.Action();
Console.WriteLine("ret = " + ret);
return ret;
}
public delegate int DoHandler2();
public static int CallWithTimeout2(DoHandler2 action, int timeoutSecond)
{
Thread threadToKill = null;
DoHandler2 wrappedAction = () =>
{
threadToKill = Thread.CurrentThread;
int ret = action();
return ret;
};
IAsyncResult result = wrappedAction.BeginInvoke(null, null);
if (result.AsyncWaitHandle.WaitOne(timeoutSecond))
{
return wrappedAction.EndInvoke(result);
}
else
{
threadToKill.Abort();
return 222;
}
}
}
输出内容:
FiveSecond
0
222
Add Result
ret = 9
9
Add Result
222
可见,如果需要退出纯C#的代码,超时后,随着Thread.Abort后,线程退出了,第二次调用FiveSecondMethodRet没有打印FiveSecod可以看出。
但是当调用C的dll时,第二次超时,thread退出了,没有打印ret=222,但是dll中还是运行完了,打印了Add Result。
有没有办法,第二次的Add Result不打印,直接退出。
这没办法,你这 PInvoke 都已经进到非托管里了,.NET 无能为力。
不过你这种情况的话可以考虑 C# 调 C 方法的时候传个 int* 的指针进去(注意要开启 unsafe),超时的时候 C# 改变指针的值,C 这面 sleep 后判断一下,好决定继不继续。