I'm playing around with the idea of creating a C++/CLI static library which exposes a number of static functions for the managed EventLogger functionality. My current Solution exists out of a /clr compiled C++ project and a C++ console app.
Visual Studio tells me that the ConsoleApp needs to be compiled with the /clr parameter but that's something I don't want to do. Is there some way of achieving what I'm trying to do here?
#### ConsoleApp.cpp
#include <iostream>
#include "../ClrEtwLogger/ClrEtwLogger.h"
int main()
{
clr_etw_logger::etw_logger::etw_log();
}
#### ClrEtwLogger.h (/clr), static library
#pragma once
#using <system.dll>
using namespace System;
using namespace Diagnostics;
namespace clr_etw_logger
{
public ref class etw_logger
{
public:
static void etw_log()
{
const auto source = gcnew String("dotNet ClrEtwLogger");
const auto log = gcnew String("EtwLogger");
const auto event = gcnew String("Sample Event 1");
if (!EventLog::SourceExists(source))
{
EventLog::CreateEventSource(source, log);
}
EventLog::WriteEntry(source, event);
EventLog::WriteEntry(source, event, EventLogEntryType::Warning, 234);
}
};
}
Related
I made a logger using spdlog which I use all over my program. But I also want to flush everything to a log file when the program is completed. How can I achieve this? I'm new to spdlog and I couldn't find proper documentation suitable for my situation.
Here are my file's:
Log.h:
#pragma once
#include "spdlog/spdlog.h"
#include "spdlog/fmt/ostr.h"
namespace Engine{
class Log{
public:
static void init();
inline static std::shared_ptr<spdlog::logger>& GetCoreLoger() { return s_CoreLogger; }
inline static std::shared_ptr<spdlog::logger>& GetClientLogger () { return s_ClientLogger;}
// I want something like this:
void flush_to_file();
private:
static std::shared_ptr<spdlog::logger> s_CoreLogger;
static std::shared_ptr<spdlog::logger> s_ClientLogger;
};
}
//Client log macros
#define VI_TRACE(...) ::Engine::Log::GetClientLogger()->trace(__VA_ARGS__)
#define VI_INFO(...) ::Engine::Log::GetClientLogger()->info(__VA_ARGS__)
#define VI_WARN(...) ::Engine::Log::GetClientLogger()->warn(__VA_ARGS__)
#define VI_ERROR(...) ::Engine::Log::GetClientLogger()->error(__VA_ARGS__)
Log.cpp:
#include "spdlog/sinks/stdout_color_sinks.h"
namespace Engine {
std::shared_ptr<spdlog::logger> Log::s_CoreLogger;
std::shared_ptr<spdlog::logger> Log::s_ClientLogger;
void Log::init() {
spdlog::set_pattern("%^[%T] %n: %v%$");
s_CoreLogger = spdlog::stdout_color_mt("VIO");
s_CoreLogger->set_level(spdlog::level::trace);
s_ClientLogger = spdlog::stdout_color_mt("APP");
s_ClientLogger->set_level(spdlog::level::trace);
}
// This is what I want:
void Log::flush_to_file(){
spdlog::write_to_file(); // Something like this
}
};
I want everything that spdlog have logged so far to be written into the file when I call that function. Is this possible? If so how can I do it?
What you probably need it is a multisink logger, a sink for stdout as you already have and a new one to also log to a file.
auto console_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
auto basic_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>("mylog.log");
std::vector<spdlog::sink_ptr> sinks{console_sink, basic_sink};
auto logger = std::make_shared<spdlog::logger>("main", sinks.begin(), sinks.end());
spdlog::register_logger(logger); //if it would be used in some other place
Your function flush_to_file probably should call
logger->flush();
To see the file content before the logger shutdown or automatic flush, try to add:
logger->flush_on(spdlog::level::info);
I am using the ODAC components designed by the devart company with embarcadero C++ builder 10.2. Now I want to define an own class, which should be able to establish a connection to an oracle database. So I want to use the TOraSession component inside of my own class but without using the VCL.
The class is defined as written below:
#include "ora.hpp"
#include "Dbaccess.hpp"
class ConnectToDatabase
{
public:
ConnectToDatabase();
virtual ~ConnectToDatabase();
bool getConnected() const {return connected_;};
void establishConnection();
protected:
TOraSession *OraSession;
std::string server_;
std::string username_;
std::string password_;
bool connected_;
std::string port_;
std::string sid_;
std::string servername_;
};
My corresponding cpp-File looks like below:
#pragma link "DBAccess"
#include "connectToDatabase.h"
void ConnectToDatabase::establishConnection()
{
OraSession = new TOraSession(NULL);
OraSession->Options->Direct = True;
OraSession->Server = server_.c_str();
OraSession->Username = username_.c_str();
OraSession->Password = password_.c_str();
if (OraSession->Connected)
{
connected_ = true;
}
else
{
connected_ = false;
}
}
The problem is, that an error occurs while C++ builder tries to link my program.
Error-message:
'DATA.DBCONSTS.OBJ can't be opened'
So I hope, that someone has an idea, what is going wrong.
best regards
Hoeh
Q: Can I make a native client that consumes a .NET COM component, using reg free COM?
I.e. Can I make a C++ application with COM info in its manifest, instead of registering the component (COM 'stuff') in the registry?
Note: I started looking at reg free COM with this question in mind, and didn't find a quick answer via web search. When I found the answer, thought'd I'd post on SO in case helps anyone else searching...
Yes.
And here's an MSDN has an article (from 2005) that walks through a working example of:
...the registration-free activation of a .NET Framework-based component by native clients via COM interop. (11 printed pages)
https://msdn.microsoft.com/en-us/library/ms973915.aspx (accessed Jan 2015)
Daryn,
This blog entry explain you how to do it:
http://blogs.msdn.com/b/rodneyviana/archive/2015/08/24/pure-native-c-consuming-net-classes-without-com-registration.aspx
Basically you use a entry point to return the pointer to the interface (using DllExport Nuget for "export"):
using System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using RGiesecke.DllExport;
namespace ContosoCom
{
public static class Exports
{
[DllExport(CallingConvention = CallingConvention.Cdecl)]
public static void GetClass([Out] [MarshalAs((UnmanagedType.Interface))] out IMyClass pInterface)
{
pInterface = new MyClass();
}
}
[ComVisible(true)]
[System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIUnknown)]
public interface IMyClass
{
void DisplayMessageBox([MarshalAs(UnmanagedType.BStr)] string Text);
void GetTicksAndDate([Out] out MyStruct Structure);
}
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, Pack = 8)]
public struct MyStruct
{
public long TicksOfNow;
public int Day;
public int Month;
public int Year;
}
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComDefaultInterface(typeof(IMyClass))]
public class MyClass : IMyClass
{
public void DisplayMessageBox(string Text)
{
MessageBox.Show(Text);
}
public void GetTicksAndDate(out MyStruct Structure)
{
Structure.TicksOfNow = DateTime.Now.Ticks;
Structure.Day = DateTime.Now.Day;
Structure.Month = DateTime.Now.Month;
Structure.Year = DateTime.Now.Year;
}
}
}
And this is how the C++ code looks like:
#include "stdafx.h"
#import "..\..\bin\Debug\ContosoCom.tlb" auto_rename
using namespace ContosoCom;
typedef void(*pGetClass)(IMyClass **iMyClass);
int _tmain(int argc, _TCHAR* argv[])
{
pGetClass getClass = NULL;
IMyClass *mc = NULL;
HINSTANCE hDLL = 0;
// load the DLL
hDLL = ::LoadLibrary(L"ContosoCom.dll");
::CoInitialize(NULL);
if(!hDLL)
{
printf("ERROR: Unable to load library ContosoCom.dll\n");
return -1;
}
//
// TO DO: Add code here to get an instance of MyClass
//
getClass = (pGetClass)GetProcAddress(hDLL, "GetClass");
if(!getClass)
{
printf("ERROR: Unable to find entry for GetClass()\n");
return -1;
}
getClass(&mc);
// At this point we do not have how to get a pointer even with the libray loaded
// End of TO DO
if(!mc)
{
printf("ERROR: Unable to get a pointer for MyClass\n");
return -1;
}
mc->DisplayMessageBox("Hello World from native to .NET without registration");
MyStruct st;
ZeroMemory(&st, sizeof(MyStruct));
mc->GetTicksAndDate(&st);
printf("Ticks %I64i\n",st.TicksOfNow);
printf("Today is %i/%i/%i\n",st.Month,st.Day,st.Year);
printf("SUCCESS: Leaving gracefully\n");
return 0;
}
Ok so I am trying to create a runtime DLL for another application that can be called using GetProcAddress.
So I have built the DLL in VS2012 C++ Express (Header and source below) and it returns NULL even when dll is in same folder as exe. So this lead me to believe there was an issue with the dllmain function. So I began looking through MSDN and found the following link
http://msdn.microsoft.com/en-us/library/vstudio/988ye33t.aspx
It states that it is already taken care for me when I create dll template which I did following this MSDN link.
http://msdn.microsoft.com/en-us/library/ms235636%28v=vs.80%29.aspx
So I decided to try build the DLL in release mode (thinking something about this might be the issue) but I get a linker error saying entry point must be define. It builds fine in debug mode but not in release mode. I assume I am missing something simple here.
Error 1 error LNK1561: entry point must be defined C:\Users\ProRip\Documents\Visual Studio 2012\Projects\PhantomAdapter\AdapterDLL\LINK AdapterDLL
Header
#ifdef PHANTOMADAPTER_EXPORTS
#define PHANTOMADAPTER_API __declspec(dllexport)
#else
#define PHANTOMADAPTER_API __declspec(dllexport)
#endif
#using <mscorlib.dll>
#using <system.dll>
using namespace System;
using namespace System::IO::Ports;
using namespace System::Threading;
namespace PhantomAdapter
{
class PhantomAdapter
{
public:
static PHANTOMADAPTER_API int open();
static PHANTOMADAPTER_API int close();
//static PHANTOMADAPTER_API double init(bool );
//static PHANTOMADAPTER_API int noDevices();
static PHANTOMADAPTER_API int angle(double& angle);
static PHANTOMADAPTER_API int torque(double torque);
static PHANTOMADAPTER_API int ready();
};
public ref class SerialPort
{
private:
static System::IO::Ports::SerialPort^ p1;
public:
static int openPort();
static void closePort();
static int read();
static void send(Byte data);
static int check();
};
}
Source
#include "stdafx.h"
#include "stdafx.h"
#include "PhantomAdapter.h"
#include <stdexcept>
using namespace std;
namespace PhantomAdapter
{
int PhantomAdapter::open()
{
int flag=0;
flag=SerialPort::openPort();
return flag;
}
int PhantomAdapter::close()
{
SerialPort::closePort();
return 1;
}
int PhantomAdapter::angle(double& angle)
{
SerialPort::send(0x82);
angle = (SerialPort::read() * 255) + (SerialPort::read());
angle = angle*(6.2832/512);
return 1;
}
int PhantomAdapter::torque(double torque)
{
return 1;
}
int PhantomAdapter::ready()
{
return SerialPort::check();
}
int SerialPort::openPort()
{
bool check=0;
p1 = gcnew System::IO::Ports::SerialPort();
p1->BaudRate = 57600;
p1->PortName = "COM3";
if(p1->IsOpen)
return 0;
else {
p1->Open();
return 1;
}
}
int SerialPort::check()
{
array<String^>^ serialPorts = nullptr;
int flag=0;
serialPorts = p1->GetPortNames();
for each(String^ port in serialPorts)
{
if(port=="COM3")
flag=1;
}
return flag;
}
void SerialPort::closePort()
{
p1->Close();
}
void SerialPort::send(Byte data)
{
array<unsigned char>^ buffer = gcnew array<Byte>(1);
buffer[0] = (char)data;
p1->Write(buffer,0,1);
}
int SerialPort::read()
{
return p1->ReadByte();
}
}
In your VS project, can you check the settings:
1. Configuration Properties > General > Configuration Type? Is this set to .dll in case of Release mode?
2. Configuration Properties > Linker > System > SubSystem? Is this the same between the 2 modes?
You cannot call a .NET method (and C++/CLI methods are .NET methods) from native code via LoadLibrary/GetProcAddress. .NET methods and classes are not exported that way. You will always get a NULL pointer.
What you can do:
Write a COM component in .NET to reference in plain C++
Export normal, plain C++ methods that call .NET code only internally
Import a managed dll into a managed (the same C++/CLI or C# or any other .NET language) executable by using the Assembly class and it's methods.
I have implemented the Event Handling with Boost::Signal and Boost::Bind in my managed C++ file.
Refered the Link:Boost::bind
Also I have created the function pointer in my native C++ file which is passed to my boost::Signal.Connect() as EventHandler in managed code.
The code for function which is passed as function pointer in my Native C++
std::string NativeCplus::AddFunctionPointer( std::string message )
{
return message;
}
and above function passed as boost::function object in another function NameChangeEvent() as below:
void NativeCplus::NameChangeEvent()
{
UnmanagedNameEvent* unmanagedNameEvent=new UnmanagedNameEvent();
boost::function<std::string (std::string)> f;
f=std::bind1st(std::mem_fun(&AlgebraAPI::AddFunctionPointer),this);//FunctionPointer
std::string name="abcd";
unmanagedNameEvent->AddEvent(f,name);
}
In the above code ,I have taken the boost::function and the function pointer is converted to that boost::function (f).(AM I RIGHT IN SAYING THIS?).Then the line unmanagedNameEvent->AddEvent(f,name) where boost::function(f) is passed to AddEvent(f,name) and this AddEvent(f,name) is implemented in my managed C++ code file. Below is my managed C++ Code which is being referred in the native c++ project:
//In my c++/CLI Wrapper.cpp
declspec(dllexport) void UnmanagedNameEvent::AddEvent(boost::function<std::string (std::string)> f,std::string name)
{
UnmanagedNameEvent* unmanagedNameEvent=new UnmanagedNameEvent();
unmanagedNameEvent->signalEventMessage.connect(f);
//which should be like this.. unmanagedNameEvent->signalEventMessage.connect(bind(NativeCplus::f));
}
PROBLEM is I can't use the NativeCplus class to refer to its unmanaged function (i.e.f)as that will create a round dependency of dll file.Any workaround for this?All ears for any shorter solution!!
Here is a sample of what I understand you want:
Your C++/CLI "wrapper":
Market.h:
#include <boost/function.hpp>
#include <boost/signals2.hpp>
class __declspec(dllexport) Market
{
private: boost::signals2::signal<void(double)> signalEvent;
public: void AddHandler(boost::function<void(double)> handler);
public: void Move(double value);
};
Market.cpp:
#include "Market.h"
#include <boost/function.hpp>
void Market::AddHandler(boost::function<void(double)> handler)
{
signalEvent.connect(handler);
}
void Market::Move(double value)
{
signalEvent(value);
}
And your native app, test.cpp:
#include <iostream>
#include <boost/function.hpp>
#include <boost/bind.hpp>
#include "Market.h"
class Investor
{
public: void move_handler(double value)
{
std::cout << (value >= 0 ? "Hey! I'm the best!" : "Wat! I'm losing my shirt! ") << std::endl;
}
};
int main()
{
Investor investor;
Market market;
boost::function<void(double)> move = boost::bind(&Investor::move_handler, &investor, _1);
market.AddHandler(move);
market.Move(+0.10);
market.Move(-0.10);
}
Results:
Hey! I'm the best!
Wat! I'm losing my shirt!
Hope this helps...