我正在为我的 C++ 库编写一个 DLL 包装器,以便从 C# 调用。此包装器还应具有从库中调用并在 C# 中实现的回调函数。这些函数有例如 std::vector
举个例子 让我感到困惑的一件事是:如何删除 C++ 代码中由 C# 创建的缓冲区?CallbackFunction FunctionImplementedInCSharp;
void FunctionCalledFromLib(const std::vector<unsigned char>& input, std::vector<unsigned char>& output)
{
// Here FunctionImplementedInCSharp (C# delegate) should somehow be called
}
void RegisterFunction(CallbackFunction f)
{
FunctionImplementedInCSharp = f;
}
CallbackFunction应该怎么定义,FunctionCalledFromLib里面的代码是什么?
最佳答案
至少从 Visual Studio 2013 开始,有一种安全的方法可以将回调从 C# 传递到 C++,并让 C++ 存储它们并稍后从非托管代码异步调用它们。您可以做的是创建一个托管的 C++/CX 类(例如,名为“CallbackManager”)以在映射中保存回调委托(delegate)引用,为每个引用指定一个枚举值。然后,您的非托管代码可以通过委托(delegate)的关联枚举值从托管 C++/CX CallbackManager 类检索托管委托(delegate)引用。这样您就不必存储原始函数指针,因此您不必担心委托(delegate)被移动或被垃圾收集:它在整个生命周期中都保留在托管堆中。
在 CallbacksManager.h 中的 C++ 端:
#include <unordered_map>
#include <mutex>
using namespace Platform;
namespace CPPCallbacks
{
// define callback IDs; this is what unmanaged C++ code will pass to the managed CallbacksManager class to retrieve a delegate instance
public enum class CXCallbackType
{
cbtLogMessage,
cbtGetValueForSetting
// TODO: add additional enum values as you add more callbacks
}
// defines the delegate signatures for our callbacks; these are visible to the C# side as well
public delegate void LogMessageDelegate(int level, String^ message);
public delegate bool GetValueForSettingDelegate(String^ settingName, String^* settingValueOut);
// TODO: define additional callbacks here as you need them
// Singleton WinRT class to manage C# callbacks; since this class is marked 'public' it is consumable from C# as well
public ref class CXCallbacksManager sealed
{
private:
CXCallbacksManager() { } // this is private to prevent incorrect instantiation
public:
// public methods and properties are all consumable by C# as well
virtual ~CXCallbacksManager() { }
static property CXCallbacksManager^ Instance
{
CXCallbacksManager^ get();
}
bool UnregisterCallback(CXCallbackType cbType);
void UnregisterAllCallbacks();
Delegate^ GetCallback(CXCallbackType cbType);
// define callback registration methods
RegisterLogMessageCallback(LogMessageDelegate^ cb) { RegisterCallback(CXCallbackType::cbtLogMessage, cb); }
RegisterGetValueForSettingCallback(GetValueForSettingDelegate^ cb) { RegisterCallback(CXCallbackType::GetValueForSetting, cb); }
// TODO: define additional callback registration methods as you add more callbacks
private:
void RegisterCallback(CXCallbackType cbType, Delegate^ rCallbackFunc);
typedef unordered_map<CXCallbackType, Delegate^> CALLBACK_MAP;
typedef pair<CXCallbackType, Delegate^> CBType_Delegate_Pair;
// Note: IntelliSense errors shown for static data is a Visual Studio IntellSense bug; the code below builds fine
// See http://social.msdn.microsoft.com/Forums/windowsapps/en-US/b5d43215-459a-41d6-a85e-99e3c30a162e/about-static-member-of-ref-class?forum=winappswithnativecode
static mutex s_singletonMutex;
static CXCallbacksManager^ s_rInstance;
mutex m_callbackMapMutex;
CALLBACK_MAP m_callbacksMap; // key=CallbackType, value = C# delegate (function) pointer
};
}
在 CallbacksManager.cpp 中,我们实现了由 C# 和我们的非托管 C++ 代码访问的托管 C++/CX 类:
#include <assert.h>
#include "CXCallbacksManager.h"
using namespace Platform;
namespace CPPCallbacks
{
// define static class data
CXCallbacksManager^ CXCallbacksManager::s_rInstance;
mutex CXCallbacksManager::s_singletonMutex;
// Returns our singleton instance; this method is thread-safe
CXCallbacksManager^ CXCallbacksManager::Instance::get()
{
s_singletonMutex.lock();
if (s_rInstance == nullptr)
s_rInstance = ref new CXCallbacksManager(); // this lives until the application terminates
s_singletonMutex.unlock();
return s_rInstance;
}
// Register a C# callback; this method is thread-safe
void CXCallbacksManager::RegisterCallback(const CXCallbackType cbType, Delegate^ rCallbackFunc)
{
_ASSERTE(rCallbackFunc);
m_callbackMapMutex.lock();
m_callbacksMap.insert(CBType_Delegate_Pair(cbType, rCallbackFunc));
m_callbackMapMutex.unlock();
}
// Unregister a C# callback; this method is thread-safe
// Returns: true on success, false if no callback was registered for callbackType
bool CXCallbacksManager::UnregisterCallback(const CXCallbackType cbType)
{
m_callbackMapMutex.lock();
const bool bRemoved = (m_callbacksMap.erase(cbType) > 0);
m_callbackMapMutex.unlock();
return bRemoved;
}
// Unregister all callbacks; this method is thread-safe
void CXCallbacksManager::UnregisterAllCallbacks()
{
// must lock the map before iterating across it
// Also, we can't change the contents of the map as we iterate across it, so we have to build a vector of all callback types in the map first.
vector<CXCallbackType> allCallbacksList;
m_callbackMapMutex.lock();
for (CALLBACK_MAP::const_iterator it = m_callbacksMap.begin(); it != m_callbacksMap.end(); it++)
allCallbacksList.push_back(it->first);
for (unsigned int i = 0; i < allCallbacksList.size(); i++)
{
CALLBACK_MAP::const_iterator it = m_callbacksMap.find(allCallbacksList[i]);
if (it != m_callbacksMap.end()) // sanity check; should always succeed
UnregisterCallback(it->first);
}
m_callbackMapMutex.unlock();
}
// Retrieve a registered C# callback; returns NULL if no callback registered for type
Delegate^ CXCallbacksManager::GetCallback(const CXCallbackType cbType)
{
Delegate^ rCallbackFunc = nullptr;
m_callbackMapMutex.lock();
CALLBACK_MAP::const_iterator it = m_callbacksMap.find(cbType);
if (it != m_callbacksMap.end())
rCallbackFunc = it->second;
else
_ASSERTE(false); // should never happen! This means the caller either forgot to register a callback for this cbType or already unregistered the callback for this cbType.
m_callbackMapMutex.unlock();
return rCallbackFunc;
}
}
委托(delegate)实例仍由我们的 CXCallbacksManager 类存储在托管堆中,因此现在可以轻松安全地在 C++ 端存储回调,以便非托管代码稍后异步调用。下面是C#端注册两个回调:
using CPPCallbacks;
namespace SomeAppName
{
internal static class Callbacks
{
// invoked during app startup to register callbacks for unmanaged C++ code to invoke asynchronously
internal static void RegisterCallbacks()
{
CPPCallbacks.CXCallbacksManager.Instance.RegisterLogMessageCallback(new LogMessageDelegate(LogMessageDelegateImpl));
CPPCallbacks.CXCallbacksManager.Instance.RegisterGetValueForSettingCallback(new GetValueForSettingDelegate(GetValueForSettingDelegateImpl));
// TODO: register additional callbacks as you add them
}
//-----------------------------------------------------------------
// Callback delegate implementation methods are below; these are invoked by C++
// Although these example implementations are in a static class, you could also pass delegate instances created
// from inside a non-static class, which would maintain their state just like any other instance method (i.e., they have a 'this' object).
//-----------------------------------------------------------------
private static void LogMessageDelegateImpl(int level, string message)
{
// This next line is shown for example purposes, but at this point you can do whatever you want because
// you are running in a normal C# delegate context.
Logger.WriteLine(level, message);
}
private static bool GetValueForSettingDelegateImpl(String settingName, out String settingValueOut)
{
// This next line is shown for example purposes, but at this point you can do whatever you want because
// you are running in a normal C# delegate context.
return Utils.RetrieveEncryptedSetting(settingName, out settingValueOut);
}
};
}
最后,这里是如何从非托管 C++ 代码调用已注册的 C# 回调:
#include <assert.h>
#include <atlstr.h> // for CStringW
#include "CXCallbacksManager.h"
using namespace CPPCallbacks;
// this is an unmanaged C++ function in the same project as our CXCallbacksManager class
void LogMessage(LogLevel level, const wchar_t *pMsg)
{
_ASSERTE(msg);
auto rCallback = static_cast<LogMessageDelegate^>(CXCallbacksManager::Instance->GetCallback(CXCallbackType::cbtLogMessage));
_ASSERTE(rCallback);
rCallback(level, ref new String(pMsg)); // invokes C# method
}
// this is an unmanaged C++ function in the same project as our CXCallbacksManager class
// Sets settingValue to the value retrieved from C# for pSettingName
// Returns: true if the value existed and was set, false otherwise
bool GetValueForSetting(const wchar_t *pSettingName, CStringW &settingValue)
{
bool bRetCode = false;
auto rCallback = static_cast<GetValueForSettingDelegate^>(CXCallbacksManager::Instance->GetCallback(CXCallbackType::cbtGetValueForSetting));
_ASSERTE(rCallback);
if (rCallback) // sanity check; should never be null
{
String^ settingValueOut;
bRetCode = rCallback(ref new String(pSettingName), &settingValueOut);
// store the retrieved setting value to our unmanaged C++ CStringW output parameter
settingValue = settingValueOut->Data();
}
return bRetCode;
}
这一切都有效,因为虽然您不能将托管委托(delegate)引用作为成员变量存储在非托管 类中,但您仍然可以从非托管代码中检索和调用托管委托(delegate),这就是上面两个 native C++ 方法可以。
关于c# - 为 C++ DLL 实现回调 C# 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3365639/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我正在阅读SandiMetz的POODR,并且遇到了一个我不太了解的编码原则。这是代码:classBicycleattr_reader:size,:chain,:tire_sizedefinitialize(args={})@size=args[:size]||1@chain=args[:chain]||2@tire_size=args[:tire_size]||3post_initialize(args)endendclassMountainBike此代码将为其各自的属性输出1,2,3,4,5。我不明白的是查找方法。当一辆山地自行车被实例化时,因为它没有自己的initialize方法
如何在ruby中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只