C#如何强制退出运行的C语言DLL

新手上路,请多包涵

使用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不打印,直接退出。

阅读 4.6k
2 个回答

这没办法,你这 PInvoke 都已经进到非托管里了,.NET 无能为力。

不过你这种情况的话可以考虑 C# 调 C 方法的时候传个 int* 的指针进去(注意要开启 unsafe),超时的时候 C# 改变指针的值,C 这面 sleep 后判断一下,好决定继不继续。

创建 System.Threading.Thread 线程,再调用 DLL。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进