首页 > 解决方案 > P/Invoke runtime STD error not redirected to file

问题描述

I am working with a C++ DLL that I access with P/Invokes from C# code. The problem is I cannot redirect the std error that happens in the unmanaged side to a file. This works well if the build is in DEBUG but when the build is in RELEASE the STD logfile-unmanaged does not contain the error but contains and does not close the application, it keeps running like there was no error:

Before Error
After Error

In C++ the STD error is redirected to a file like this:

extern "C" __declspec(dllexport) void RedirectStd()
{
    int fileNO = _fileno(stderr);
    _close(fileNO);
    int file = _open("logfile-unmanaged", O_CREAT | O_RDWR, 0644);
    _dup2(file, fileNO);
}

A runtime error in C++ is generated like this:

extern "C" __declspec(dllexport) void DoException()
{
    fprintf(stderr, "Before Error\n");
    int a = 0;
    int b = 10 / a;
    fprintf(stderr, "After Error\n");
}

In C# I'm calling both of those methods:

    [DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
    internal static extern void RedirectStd();

    [DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
    internal static extern void DoException();

My Main function:

    [HandleProcessCorruptedStateExceptions]
    [SecurityCritical]
    static void Main(string[] args) {

        Console.WriteLine("===== REDIRECT =====");
        Console.WriteLine("Native/Unmanaged exception redirected to logfile-unmanaged.");
        RedirectStd();
        Console.WriteLine("Native/Unmanaged std error redirected.");
        Console.WriteLine("");

        Console.WriteLine("===== EXCEPTION =====");

        DoException();

        Console.WriteLine("Waiting for program to crash");
        do {

        } while (true);
    }

EDIT:

C# console application: program.cs

using System;
using System.Runtime.ExceptionServices;
using System.Runtime.InteropServices;
using System.Security;

namespace ConsoleWithErrors {
    class Program {
        [DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        internal static extern void RedirectStd();

        [DllImport("TestError.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
        internal static extern void DoException();

        [HandleProcessCorruptedStateExceptions]
        [SecurityCritical]
        static void Main(string[] args) {
            RedirectStd();
            DoException();

            Console.WriteLine("Waiting for program to crash");
            do {

            } while (true);
        }
    }
}

The console application stays opened like there was no error on the unmanaged side.

标签: c#c++pinvoke

解决方案


如果构建在 DEBUG 中,这很有效,但是当构建在 RELEASE 中时,STD logfile-unmanaged 不包含错误

它可能已优化,因为您不使用除以零的结果。尝试这样的事情:

extern "C" __declspec(dllexport) int DoException()
{
    fprintf(stderr, "Before Error\n");
    int a = 0;
    int b = 10 / a;
    fprintf(stderr, "After Error\n");

    return b;
}

这将使结果脱离内部链接并强制编译器发出代码。

但是,您似乎是 C# P/Invoke 的新手,所以这里有一些提示:

  • SetLastError属性指示编组器该函数将使用 Win32 APISetLastError设置其错误状态,并且您的函数绝对不会这样做。您应该删除它,因为对编组者撒谎绝不是成功的秘诀。
  • SecurityCritical属性与信任级别之间的进程提升有关。同样,您的应用程序与此无关,应该被删除。
  • HandleProcessCorruptedStateExceptions自 .Net Core(包括 .Net 5)以来,该属性已被弃用。因此,如果您依赖它,那么您的程序就快到了生命的尽头,而且您似乎甚至还没有开始编写它。好消息是除以 0 不是需要处理此属性的异常之一,因此您应该再次删除它!

推荐阅读