NUnit does not capture output of std::cerr - c#

I have an nunit Test in C#, that calls a C# wrapper of a function in a C++ DLL.
The C++ code uses std::cerr to output various messages.
These messages cannot be redirected using nunit-console /out /err or /xml switch.
In nunit (the GUI version) the output does not appear anywhere.
I would like to be able to see this output in nunit (GUI version).
Ideally I would like to be able to access this output in the Test.
Thanks for any help.

Redirecting std::cerr is a matter of replacing the stream buffer with your own.
It is important to restore in original buffer before we exit. I don't know what your wrapper looks like, but you can probably figure out how to make it read output.str().
#include <iostream>
#include <sstream>
#include <cassert>
using namespace std;
int main()
{
streambuf* buf(cerr.rdbuf());
stringstream output;
cerr.rdbuf(output.rdbuf());
cerr << "Hello, world!" << endl;
assert(output.str() == "Hello, world!\n");
cerr.rdbuf(buf);
return 0;
}

Thanks for the hint.
This is what I ended up doing:
.CPP file ------------------------
#include <iostream>
#include <sstream>
static std::stringstream buffer;
static std::streambuf * savedBuffer = NULL;
extern "C" __declspec(dllexport) bool Redirect()
{
if (savedBuffer)
{
return false;
}
std::streambuf * buf(std::cerr.rdbuf());
std::cerr.rdbuf(buffer.rdbuf());
// This two lines are for illustration purposes only!
std::cerr << "Hello world" << std::endl;
return true;
}
extern "C" __declspec(dllexport) void Revert()
{
if (savedBuffer)
{
std::cerr.rdbuf(savedBuffer);
}
savedBuffer = NULL;
}
extern "C" __declspec(dllexport) const char * getCerr()
{
return _strdup(buffer.str().c_str());
}
extern "C" __declspec(dllexport) void freeCharPtr(char *ptr)
{
free(ptr);
}
.CS file ------------------------------------------
public static class Redirector
{
// PRIVATE ------------------------------------------------------------
private const String LibraryName = "MyCpp.dll";
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
private static extern IntPtr getCerr();
// PUBLIC -------------------------------------------------------------
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern bool Redirect();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
public static extern void Revert();
[DllImport(LibraryName, CharSet = CharSet.Ansi)]
internal static extern void freeCharPtr(IntPtr ptr);
public static string GetCerr()
{
IntPtr temp = getCerr();
string result = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(temp);
freeCharPtr(temp);
return result;
}
}
NUnit Test -----------------------
[Test]
// [Ignore]
public void TestRedirect()
{
Redirector.Redirect();
// Call more functions that output to std::cerr here.
Redirector.Revert();
System.Console.WriteLine(Redirector.GetCerr());
}
The freeCharPtr() stuff is necessary to free the allocated memory from _strdup(), since I could not work out (if it's even possible) how to marshal an std::string.
Note: This is not thread safe!

Related

GO redirect STD error from C# managed side

I have a test code that has this stack C#->C->GO->C#(ERROR HERE).
Whenever an error happens on C# and if that C# code is called by GOLANG the error is not redirected to error.std file. However, if I start the code from VS with the attached debugger the error is redirected to the file.
C#:
Program.cs
class Program {
[DllImport("gotestDLL.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void Error();
[DllImport("gotestDLL.dll", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern void SetCSharpPointerHandler(IntPtr ptr);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate IntPtr CSharpPointerHandler(IntPtr message);
private static CSharpPointerHandler _messageHandlerCb = new CSharpPointerHandler(SomeMethodWithError);
private static IntPtr _messageHandlerCbPtr = Marshal.GetFunctionPointerForDelegate(_messageHandlerCb);
static void Main(string[] args) {
SetCSharpPointerHandler(_messageHandlerCbPtr);
Console.WriteLine("|-C#->START OF C#");
Error();
Console.WriteLine("|-C#->Waiting for program to crash");
do {
} while (true);
}
private static IntPtr SomeMethodWithError(IntPtr messagePtr) {
Console.WriteLine("|-C#->Back to C#");
var a = 0;
Console.WriteLine("|-C#->ERROR HAPPENS HERE C#");
var b = 10 / a;
Console.WriteLine(b);
return IntPtr.Zero;
}
}
C:ctestdll.C
#include <stdio.h>
#include "gotestDLL.h"
void Error() {
printf("|-C--->START OF C\n");
RedirectStdError();
printf("|-C--->START ERROR\n");
StartError();
printf("|-C--->END START ERROR\n");
printf("|-C-->END OF C\n");
}
int main() {
}
H:ctestDLL.h
#include <stdio.h>
#include <stdbool.h>
typedef char* CSharpPointerHandler(char* query);
static inline char* RunCSharpPointerHandler(CSharpPointerHandler* handler, char* in) {
return handler(in);
}
#ifdef __cplusplus
extern "C"
{
#endif
__declspec(dllexport) void SetCSharpPointerHandler(CSharpPointerHandler* handler);
__declspec(dllexport) void Error();
#ifdef __cplusplus
}
#endif
GO:gotestDLL.go
package main
/*
#include<stdio.h>
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include "ctestDLL.h"
*/
import "C"
import "fmt"
import "os"
var queryHandler *C.CSharpPointerHandler = nil
//export SetCSharpPointerHandler
func SetCSharpPointerHandler(handler *C.CSharpPointerHandler) {
queryHandler = handler
}
//export StartError
func StartError() {
inc := C.CString("TEST")
fmt.Println("|-GO--->Calling Handler from GO->C#")
C.RunCSharpPointerHandler(queryHandler, inc)
fmt.Println("|-GO--->END Calling Handler from GO")
}
//export RedirectStdError
func RedirectStdError() {
f, err := os.OpenFile("error.std", os.O_WRONLY | os.O_CREATE | os.O_SYNC, 0777)
if err != nil {
fmt.Println("os.OpenFile")
}
newfd := C._open_osfhandle(C.intptr_t(f.Fd()), C._O_APPEND | C._O_TEXT)
if newfd == C.int(-1) {
fmt.Println("failed to create osfhandle: _open_osfhandle(%v, _O_APPEND|_O_TEXT)", C.intptr_t(f.Fd()))
}
stderr_fd := C._fileno(C.stderr)
forwarded := C._dup2(newfd, stderr_fd)
if forwarded == C.int(-1) {
fmt.Println("failed to duplicate stderr fd: _dup2(%v, %v): %v", newfd, stderr_fd, forwarded)
}
os.Stderr = f
}
func main() {
// Need a main function to make CGO compile package as C shared library
}
OUTPUT:
|-C#->START OF C#
|-C--->START OF C
|-C--->START ERROR
|-GO--->Calling Handler from GO->C#
|-C#->Back to C#
|-C#->ERROR HAPPENS HERE C#

Log4net, sending log messages from a c++ dll to a c# application?

I have a WPF / c# application that uses Log4Net for logging. This application calls a few c++ dlls using:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void TestFunction();
What I would like to do is have the dlls send logging messages back to the C# application, so that everything from both c++ and c# go to the same log. Is this possible?
If so, how can i go about it?
An example that redirect logs in c++ dill to c# callback:
c# side:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
delegate void LogCallback([MarshalAs(UnmanagedType.LPWStr)] string info);
namespace WinApp
{
static class Wrapper
{
[DllImport("target.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void SetLogCallback([MarshalAs(UnmanagedType.FunctionPtr)] LogCallback callbackPointer);
internal static void Init()
{
LogCallback log_cb = (info) =>
{
Console.Write(DateTime.Now.TimeOfDay.ToString() + " " + info);
};
SetLogCallback(log_cb);
}
}
c++ side( compiled in target.dll):
extern "C" {
typedef char* (__stdcall* LogCallback)(const wchar_t* info);
PEPARSER_API void SetLogCallback(LogCallback cb);
}
static LogCallback global_log_cb = nullptr;
void LogInfo(const wchar_t* info) {
if (global_log_cb) {
std::wstring buf = L"[PEParse]" + std::wstring(info) + L"\n";
global_log_cb(buf.c_str());
}
else {
std::cout << "global_log_cb not set\n";
}
}
void LogInfo(const char* info) {
const size_t cSize = strlen(info) + 1;
size_t t;
std::wstring wstr(cSize, L'#');
mbstowcs_s(&t, &wstr[0], cSize, info, cSize - 1);
LogInfo(&wstr[0]);
}
void SetLogCallback(LogCallback cb) {
global_log_cb = cb;
LogInfo("log init");
}
I have been using this interface for long times.

Dllimport passing string from C# to C++

I'm trying to pass a string from C# to C++, using platform invoke.
C++ code:
#include<string>
using namespace std;
extern "C"
{
double __declspec(dllexport) Add(double a, double b)
{
return a + b;
}
string __declspec(dllexport) ToUpper(string s)
{
string tmp = s;
for(string::iterator it = tmp.begin();it != tmp.end();it++)
(*it)-=32;
return tmp;
}
}
C# code:
[DllImport("TestDll.dll", CharSet = CharSet.Ansi, CallingConvention =CallingConvention.Cdecl)]
public static extern string ToUpper(string s);
static void Main(string[] args)
{
string s = "hello";
Console.WriteLine(Add(a,b));
Console.WriteLine(ToUpper(s));
}
I receive a SEHException. Is it impossible to use std::string like this? Should I use char* instead ?
Сorrect decision
C# side:
[DllImport("CppDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetString(string s);
public string GetString_(string s)
{
var ptr = GetString(s);
var answerStr = Marshal.PtrToStringAnsi(ptr);
return answerStr;
}
C++ side:
extern "C" __declspec(dllexport) const char* GetString(char* s)
{
string workStr(s);
int lenStr = workStr.length() + 1;
char* answer = new char[lenStr];
const char * constAnswer = new char[lenStr];
strcpy(answer, workStr.c_str());
constAnswer = answer;
return constAnswer;
}
And disable /sdl- in the settings of the cpp project.
One way of doing it without causing a memory leak is using a callback.
C# side:
private delegate bool DLLCallback(IntPtr message);
[DllImport(#"YourLibrary.dll", SetLastError = true, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Auto)]
private static extern void Receive(DLLCallback callback);
private static bool callback(IntPtr ptr)
{
string result = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine(result);
// If the Heap is used
// Marshal.FreeHGlobal(ptr);
return true;
}
private static void Main(string[] args) {
Receive(callback);
}
C++ side:
extern "C" {
typedef BOOL(__stdcall* OutputCallback)(const char* str);
__declspec(dllexport) void Receive(OutputCallback callback)
{
char buffer[BUFFER_SIZE];
ZeroMemory(buffer, BUFFER_SIZE);
BOOL callbackResult = callback(buffer);
}
}
There are other options. This is a good article about passing strings between managed and unmanaged code: article
I suggest to use char*. Here a possible solution.
If you create another C# function ToUpper_2 as follows
C# side:
[DllImport("TestDll.dll"), CallingConvention = CallingConvention.Cdecl]
private static extern IntPtr ToUpper(string s);
public static string ToUpper_2(string s)
{
return Marshal.PtrToStringAnsi(ToUpper(string s));
}
C++ side:
#include <algorithm>
#include <string>
extern "C" __declspec(dllexport) const char* ToUpper(char* s)
{
string tmp(s);
// your code for a string applied to tmp
return tmp.c_str();
}
you are done!

How to load a Dll (c++) into at C# Application at runime

I have this code in a c++ file, with compiles to a dll.
#include "stdafx.h"
#include "WHUU.h"
#include "stdafx.h"
typedef int (__stdcall * Callback)(const int text);
static int x , y= 0;
Callback Handler = 0;
int getX()
{
return x;
}
void setX(int i)
{
x = i;
}
void setY(int i)
{
y = i;
}
int getY()
{
return y;
}
extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
Handler = handler;
}
extern "C" __declspec(dllexport)
void __stdcall addX(int x) {
setX(x);
}
extern "C" __declspec(dllexport)
void __stdcall addY(int y) {
setY(y);
}
extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
int z = getX() + getY();
int retval = Handler(z);
}
My c# application now has to load this dll on runtime. Add to the callback and call the functions. I dont want to use a class. I could load the class and with
Type[] types = moduleAssembly.GetTypes();
But this overkill! Also c++ is not managed.
I mean its so tiny! (and yes this is an example , but the "real" is just as big as this example).
How do i do that?
Thanks you for your help!
add:
i dont like frameworks (like pinvoke / assembly)
the function names / types are fixed and will never change (think of a driver.dll read write)
this dll is written by customers so it should be as easy as possible!
You could also do it over p/invoke, as example:
[DllImport("unmanaged.dll", CharSet = CharSet.Ansi)]
private extern static int yourFunction(int var1, int var2);
i dont like frameworks (like pinvoke / assembly)
I'll suggest P/Invoke anyway. I don't think there are any reasonable alternatives. Perhaps writing a managed wrapper in managed C++, but really?
When you use P/Invoke the .NET runtime will dynamically load the DLL's that you have specified (and this is done when you first call the function). There is no reason to use P/Invoke to call LoadLibrary first.
You can either use P/Invoke to call the dll directly, or you can create a C++/CLI wrapper for it.
Here's how you can do it using P/Invoke:
Add the compiled dll to your C# project, set its 'Copy to Output Directory' property to 'Copy Always'. and call it like this:
class Program
{
[DllImport("cppdll.dll")]
private static extern void addX(int x);
[DllImport("cppdll.dll")]
private static extern void addY(int y);
static void Main(string[] args)
{
addX(5);
addY(7);
}
}
One way to do this would be through API. Find below an example for the setX and getX method from your sample above.
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr LoadLibraryEx(string libraryPath, IntPtr fileHandle, int actionFlag);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern bool FreeLibrary(IntPtr libraryHandle);
[DllImport("kernel32.dll", CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName);
IntPtr ptr = IntPtr.Zero;
private delegate void setX (int i);
private delegate int getX();
private setX setXDel;
private getX getXDel;
public Constructor()
{
loadLib();
}
public void YourMethod()
{
setXDel(100);
int y = getXDel();
Console.WriteLine(y.ToString());
}
private void loadLib()
{
string path = "your dll path";
ptr = LoadLibraryEx(path, IntPtr.Zero, 0);
if (ptr == IntPtr.Zero)
throw new Exception("Cannot load dll.");
IntPtr addressPtr = GetProcAddress(ptr, "setX");
setXDel = (setX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(setX));
addressPtr = GetProcAddress(unrarPtr, "getX");
getXDel = (getX)Marshal.GetDelegateForFunctionPointer(addressPtr, typeof(getX));
}
public void Dispose()
{
if (ptr != IntPtr.Zero)
{
FreeLibrary(ptr);
}
}
I used P/Invoke, post my own cause the callback was not included by the others. But they were good answers! The C# Applicationhas use this GUI.
The Code for the Application is this : (mind it is a prototype to answer a technical question)
public partial class Form1 : Form
{
private delegate int Callback(int text);
private Callback mInstance; // Ensure it doesn't get garbage collected
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void SetCallback(Callback fn);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addX(int x);
[DllImport(#"C:\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void addY(int y);
[DllImport(#"C:\\Visual Studio 2010\Projects\kalkulatorius\Debug\WHUU1.dll")]
private static extern void TestCallback();
private int Handler(int text)
{
textBox3.Text = text.ToString();
return 42;
}
private void button1_Click(object sender, System.EventArgs e)
{
mInstance = new Callback(Handler); // to set the callback in lib
SetCallback(mInstance); // could also be withhin constructor!
addX(int.Parse(textBox1.Text)); // other people in this thread posted this correct answer
addY(int.Parse(textBox2.Text));
TestCallback();
}
public Form1()
{
InitializeComponent();
}
So if you want to use a c++ lib with functions and callbacks (like in the question posted) you can use this.
But how to change the lib?
The Path to the lib is hardcoded (see [dllImport ......] ). But you
can swop the lib in filesystem. This can happen AFTER you build this
application.
If the lib is not on the given path an exception is thrown. So if
your program wants to use this lib first check the lib is present on the filesystem.
(not included in this simple prototype)
So to switch functionality (swop a driver lib ect) you copy a different lib with the
same name into the given path. (Copy paste - maybe change name)
This solution has the least overhead and is good if you never change the interface of an dll after the application was build!

What is wrong with my INTEROP snippet?

What's wrong with this? I can't seem to figure out how to change it. Please help....!!!!
Here's the error message:
A call to PInvoke function 'MyClassName::Process' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
#include "stdafx.h"
#include "TestDll.h"
extern "C" __declspec(dllexport) void Process(lpUnmagedStruct lpStruct, int size)
{
lpStruct[0].a = 0;
lpStruct[0].b = 0;
lpStruct[1].a = 1;
lpStruct[1].b = 1;
}
typedef struct
{
double a;
double b;
}UnmanagedStruct, far *lpUnmagedStruct;
extern "C" __declspec(dllexport) void Process(lpUnmagedStruct lpStruct, int size);
And here is my .NET codes:
[DllImport("TestDLL.dll", EntryPoint = "Process", CharSet = CharSet.Ansi)]
internal static extern void Process([In, Out] ManagedStruct[] aStruct, int size );
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class ManagedStruct
{
public double a;
public double b;
}
const int size = 3;
ManagedStruct[] aStruct = new ManagedStruct[size];
Process(aStruct, size);
I suspect you need to add the calling convention:
[DllImport("TestDLL.dll",
EntryPoint = "Process",
CharSet = CharSet.Ansi,
CallingConvention=CallingConvention.Cdecl)]

Categories

Resources