我们有一个C#程序员,希望.NET对象完成所有基础工作。它应该基本上是一个带有功能和事件的黑盒子。
我用C ++ Builder编写了所有这些,使用非可视VCL类,现在看来我必须用它来制作一个.NET对象。
我需要一个简单的例子来说明如何创建一个.NET"框"使用一个函数和一个事件处理程序,从那里我应该能够实现其余部分。我应该在COM对象中执行此操作吗?我应该使用什么技术?
示例C ++方面。
<?php
// Establish connection to the server
$mysqli = mysqli_connect("localhost", "root", "");
// Selecting Database
$db = mysqli_select_db($mysqli, "p00702");
// Starting session
session_start();
// Storing Session
$user_check = $_SESSION['login_user'];
$_SESSION['user_id'] = userid;
$_SESSION['user_firstname'] = firstname;
// Test to see the content of the global session variable
print_r($_SESSION);
// SQL Query To Fetch Complete Information Of User
$ses_sql = mysqli_query($mysqli, "SELECT username FROM logins WHERE username='$user_check'");
$row = mysqli_fetch_assoc($ses_sql);
$login_session = $row['username'];
if (!isset($login_session)) {
// Closing Connection
mysqli_close($mysqli);
// Redirecting To Home Page
header('Location: index.php');
// Make sure that codes below do not execut upon redirection.
exit;
}
我删除了所有内部内容,只留下了应该可以从C#中获取的公开函数,事件和属性。
从这个类我需要创建一个这样的.NET对象:
typedef void __fastcall (__closure *TIntEvent)(int Status);
typedef void __fastcall (__closure *TVoidEvent)(void);
typedef void __fastcall (__closure *TResultEvent)(String cmd, int code);
typedef void __fastcall (__closure *TModeEvent)(int mode, int reason);
class TDevice : public TObject {
private:
// properties
String FPortName;
String FDevice;
String FComment;
String FID;
double FBootware;
double FFirmware;
protected:
public:
// properties
__property String PortName = { read=FPortName };
__property String Device = { read=FDevice };
__property String Comment = { read=FComment };
__property String ID = { read=FID };
__property double Bootware = { read=FBootware };
__property double Firmware = { read=FFirmware };
// event function pointers
TModeEvent OnMode;
TIntEvent OnStatus;
TIntEvent OnSensors;
TVoidEvent OnInfo;
TResultEvent OnResult;
// public interface
bool Connect(void);
void Disconnect(void);
void Reset(void);
void Boot(void);
void GetInfo(void);
void GetTag(void);
};
我还需要C#将函数连接到C ++类中的Event处理程序。
MyLib.IDevice.Connect();
MyLib.IDevice.Disconnect();
MyLib.IDevice.Reset();
MyLib.IDevice.Boot();
MyLib.IDevice.GetInfo();
MyLib.IDevice.GetTag();
在C ++类中调用这些事件处理程序来触发这样的事件:
MyLib.IDevice.OnMode = CSharpEventHandler1;
MyLib.IDevice.OnStatus = CSharpEventHandler2;
MyLib.IDevice.OnSensors = CSharpEventHandler3;
MyLib.IDevice.OnInfo = CSharpEventHandler4;
MyLib.IDevice.OnResult = CSharpEventHandler5;
还有一些属性,但这些属性很容易在COM界面中形成(如果这是我们需要的)......
由于这是用C ++编写的,C ++构建器可以编写组件(对于C ++ Builder和Delphi,使用ActiveX技术),也许可以将C ++ Builder组件库转换为.Net对象/组件?
编辑: 为了使它更清晰......
MyLib.IDevice.Connect()是我希望C#看到的...函数列表是C ++函数,如.Net对象MyLib和接口IDevice。
假设我已经创建了一个MyLib.IDevice实例作为Device,我可以调用Device.Connect();来自C#。
答案 0 :(得分:3)
很难......丑陋......最简单的解决方案可能就是创建一个C接口:
extern "C"
{
__declspec(dllexport) __stdcall TDevice* NewDevice()
{
return new TDevice();
}
__declspec(dllexport) void __stdcall DeleteDevice(TDevice *pDevice)
{
delete pDevice;
}
__declspec(dllexport) bool __stdcall ConnectDevice(TDevice *pDevice)
{
return pDevice->Connect();
}
.. and so on
}
在C#中:
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern IntPtr NewDevice();
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern void DeleteDevice(IntPtr pDevice);
[DllImport("YourDll.dll", CallingConvention = CallingConvention.Stdcall)]
public static extern bool ConnectDevice(IntPtr pDevice);
... and so on
如果你对此感到满意,我们可以开始谈论传递代表......这将是一种痛苦,相信我: - )
Uff ......这很长... C ++方面,如果你为你的班级创建一个包装器会更好。这是因为您在事件中使用__fastcall __closure
。这两个修饰符都与C#不兼容,所以你&#34; proxy&#34;他们在包装中。
// __fastcall not handled by C#
typedef void __stdcall (*TIntEventFunc)(int Status);
typedef void __stdcall (*TVoidEventFunc)(void);
typedef void __stdcall (*TResultEventFunc)(const wchar_t *cmd, int code);
typedef void __stdcall (*TModeEventFunc)(int mode, int reason);
class TDeviceWrapper {
public:
// You could even use directly a TDevice Device, depending on how your program works.
// By using a TDevice *, you can attach the wrapper to a preexisting TDevice.
TDevice *PDevice;
TModeEventFunc OnModeFunc;
TIntEventFunc OnStatusFunc;
TIntEventFunc OnSensorsFunc;
TVoidEventFunc OnInfoFunc;
TResultEventFunc OnResultFunc;
void __fastcall OnStatus(int status) {
OnStatusFunc(status);
}
void __fastcall OnResult(String cmd, int code)
{
OnResultFunc(cmd.c_str(), code);
}
};
extern "C" {
__declspec(dllexport) TDeviceWrapper* __stdcall NewDevice()
{
auto pWrapper = new TDeviceWrapper();
pWrapper->PDevice = new TDevice();
return pWrapper;
}
__declspec(dllexport) void __stdcall DeleteDevice(TDeviceWrapper *pWrapper)
{
delete pWrapper->PDevice;
delete pWrapper;
}
__declspec(dllexport) const wchar_t* __stdcall GetPortName(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->PortName.c_str();
}
__declspec(dllexport) bool __stdcall Connect(TDeviceWrapper *pWrapper)
{
return pWrapper->PDevice->Connect();
}
__declspec(dllexport) void __stdcall SetStatus(TDeviceWrapper *pWrapper, TIntEventFunc statusFunc) {
pWrapper->OnStatusFunc = statusFunc;
if (statusFunc) {
pWrapper->PDevice->OnStatus = pWrapper->OnStatus;
} else {
pWrapper->PDevice->OnStatus = nullptr;
}
}
__declspec(dllexport) void __stdcall SetResult(TDeviceWrapper *pWrapper, TResultEventFunc resultFunc) {
pWrapper->OnResultFunc = resultFunc;
if (resultFunc) {
pWrapper->PDevice->OnResult = pWrapper->OnResult;
} else {
pWrapper->PDevice->OnResult = nullptr;
}
}
}
然后C# - 你必须创建另一个包装器:-)这次因为当你传递一个代表C# - &gt; C ++时,.NET会创建一个&#34; thunk&#34; ,但是如果你不在某个地方保存代表,那么&#34; thunk&#34;收集垃圾。因此,最简单的解决方案通常是创建一个包装类,您可以在其中保存已使用的委托。您甚至可以在此包装器中封装Dispose()
模式: - )
public class TDeviceWrapper : IDisposable
{
// Fastcall not handled by C#
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TIntEventFunc(int Status);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TVoidEventFunc();
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Unicode)]
public delegate void TResultEventFunc(string cmd, int code);
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void TModeEventFunc(int mode, int reason);
IntPtr ptr;
[DllImport("TDevice.dll")]
static extern IntPtr NewDevice();
[DllImport("TDevice.dll")]
static extern void DeleteDevice(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern IntPtr GetPortName(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void Connect(IntPtr pWrapper);
[DllImport("TDevice.dll")]
static extern void SetStatus(IntPtr pWrapper, TIntEventFunc statusFunc);
[DllImport("TDevice.dll")]
static extern void SetResult(IntPtr pWrapper, TResultEventFunc resultFunc);
// To prevent the GC from collecting the managed-tounmanaged thunks, we save the delegates
TModeEventFunc modeFunc;
TIntEventFunc statusFunc;
TIntEventFunc sensorsFunc;
TVoidEventFunc infoFunc;
TResultEventFunc resultFunc;
public void Init()
{
ptr = NewDevice();
}
public string PortName
{
get
{
// Important! .NET will try to free the returned
// string if GetPortName returns directly a string.
// See for example https://limbioliong.wordpress.com/2011/06/16/returning-strings-from-a-c-api/
IntPtr ptr2 = GetPortName(ptr);
return Marshal.PtrToStringUni(ptr2);
}
}
public void Connect()
{
Connect(ptr);
}
public void SetStatus(TIntEventFunc statusFunc)
{
this.statusFunc = statusFunc;
SetStatus(ptr, statusFunc);
}
public void SetResult(TResultEventFunc resultFunc)
{
this.resultFunc = resultFunc;
SetResult(ptr, resultFunc);
}
~TDeviceWrapper()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (ptr != IntPtr.Zero)
{
DeleteDevice(ptr);
ptr = IntPtr.Zero;
}
if (disposing)
{
modeFunc = null;
statusFunc = null;
sensorsFunc = null;
infoFunc = null;
resultFunc = null;
}
}
}
然后你可以,例如:
public class MyClass
{
public void StatusEvent(int status)
{
Console.WriteLine("Status: {0}", status);
}
public void ResultEvent(string cmd, int code)
{
Console.WriteLine("Resukt: {0}, {1}", cmd, code);
}
}
和
var mc = new MyClass();
using (var wrapper = new TDeviceWrapper())
{
wrapper.Init();
wrapper.SetStatus(mc.StatusEvent);
wrapper.SetResult(mc.ResultEvent);
wrapper.Connect();
}