在C++中反射調(diào)用.NET的方法(一)
為什么要在C++中調(diào)用.NET
一般情況下,我們常常會在.NET程序中調(diào)用C/C++的程序,使用P/Invoke方式進行調(diào)用,在編寫代碼代碼的時候,首先要導(dǎo)入DLL文件,然后在根據(jù)C/C++的頭文件編寫特殊的C#平臺調(diào)用代碼,例如像下面這個樣子:
[DllImport("Interop.dll",EntryPoint = "Multiply",CharSet = CharSet.Ansi)]
static extern int Multiply(int factorA, int factorB);
詳細的過程,可以參考之前我這篇文章:《C#調(diào)用C和C++函數(shù)的一點區(qū)別》
有時候,我們也會有在C++中調(diào)用.NET的需求,比如我們在維護一個大型的C++應(yīng)用程序,它年代久遠,現(xiàn)在需要增加一些新功能,而這些功能在.NET中已經(jīng)有了,只需要調(diào)用它即可,如果為了方便想要用.NET重寫這個C++應(yīng)用程序是不太現(xiàn)實的,幸好,C++/CLI提供了一個簡便的方案使得可以在C++中直接編寫.NET程序,所以C++/CLI代表托管和本地編程的結(jié)合,可以在托管代碼中直接使用本地代碼,也可以反過來,這樣結(jié)合了C++本地代碼的高效性和.NET代碼的強大性,看起來是非常有潛力的。
使用C++/CLI進行.NET編程
要進行C++/CLI編程,只需要進行下面的步驟:
1,添加.NET程序集的應(yīng)用;
2,修改C++項目屬性,配置屬性->公共語言運行時支持-公共語言運行時支持(/clr)
然而,為了保持C++與.NET應(yīng)用程序的獨立性,要求不能將.NET的DLL文件放到C++的應(yīng)用程序目錄下,因此上述步驟1不可行,需要在C++代碼中使用反射來調(diào)用.NET。
注意,本文說的C++反射調(diào)用,不是對C++自身進行封裝的反射功能,而是在C++/CLI代碼中反射調(diào)用.NET代碼,原理上跟你在.NET應(yīng)用中反射調(diào)用另外一個.NET的程序集一個道理。
首先,我們建立一個名字叫CppNetTest的解決方案,添加3個項目:
1,CppConsoleTest---一個C++控制臺項目,在項目中更改屬性支持CLR;
2,NetApp--一個.NET控制臺應(yīng)用程序,作為對比示例代碼,方便編寫C++/CLI代碼參考;
3,NetLib--一個.NET類庫程序集,它將被1和2項目進行反射調(diào)用。
我們先在NetLib項目寫一個簡單的.NET 類,這個類的方法內(nèi)部沒有復(fù)雜的業(yè)務(wù)邏輯代碼,僅僅用來供反射調(diào)用測試:
namespace NetLib
{
public class User
{
static List<IUserInfo> UserDb = new List<IUserInfo>();
public int GetUserID(string IdString)
{
int result = 0;
int.TryParse(IdString, out result);
return result;
}
public DateTime GetUserBirthday(int userId)
{
return new DateTime(1980, 1, 1);
}
public IUserInfo GetUserByID(int userId)
{
IUserInfo userinfo= EntityBuilder.CreateEntity<IUserInfo>();
userinfo.ID = userId;
userinfo.Name = "姓名_" + userId;
userinfo.Birthday = new DateTime(1980, 1, 1);
return userinfo;
}
//返回List或者數(shù)組,不影響 C++調(diào)用
public List<IUserInfo> GetUsers(string likeName)
{
List<IUserInfo> users = new List<NetLib.IUserInfo>();
for (int i = 0; i < 10; i++)
{
IUserInfo userinfo = GetUserByID(i);
userinfo.Name += likeName;
users.Add(userinfo);
}
//return users.ToArray();
return users;
}
public bool SaveUsers(IList<IUserInfo> users)
{
UserDb.AddRange(users);
return true;
}
public IUserInfo CreateUserObject()
{
return EntityBuilder.CreateEntity<IUserInfo>();
}
public bool SaveUsers2(IEnumerable<Object> para)
{
var users = from u in para
select u as IUserInfo;
return SaveUsers (users.ToList());
}
}
}
在CppConsoleTest項目的頭文件中,添加一個 UserProxy.h 的C++頭文件,在文件中添加下面的命名空間:
using namespace System; using namespace System::Reflection; using namespace Runtime::InteropServices; using namespace System::Collections;
這樣我們就可以使用反射相關(guān)的類型了。
在UserProxy類中,先編寫我們需要的構(gòu)造函數(shù):
public ref class UserProxy
{
private:
String^ assemblyFile; //"..\\NetLib\\bin\\Debug\\NetLib.dll"
Object^ dotnetObject;
Type^ entityBuilderType;
String^ className = "NetLib.User";
EntityHelper^ helper;
public:
UserProxy(String^ assemblyFile)
{
this->assemblyFile = assemblyFile;
Assembly^ ass = Assembly::LoadFrom(this->assemblyFile);
this->dotnetObject = ass->CreateInstance(className);
String^ sodPath = System::IO::Path::Combine(System::IO::Path::GetDirectoryName(this->assemblyFile), "PWMIS.Core.dll");
/*Assembly^ ass_sod = Assembly::LoadFrom(sodPath);
this->entityBuilderType = ass_sod->GetType("PWMIS.DataMap.Entity.EntityBuilder");*/
helper = gcnew EntityHelper(sodPath);
}
}
注意我們的 C++/CLI的類必須是“引用”類型,所以需要加關(guān)鍵字 ref,即:
public ref class UserProxy{}
所有的.NET引用類型,在使用的時候,都必須在類型名字后加 ^ 符號,例如下面定一個.NET字符串類型變量:
String^ assemblyFile;
帶^符號的變量,在C++/CLI中稱為 “句柄”對象,用來跟C++本地代碼的“指針”相區(qū)別。
在C++中,類的成員用 -> 符號調(diào)用,命名空間或者類的靜態(tài)成員,用::調(diào)用,例如上面的構(gòu)造函數(shù)中的代碼:
Assembly^ ass = Assembly::LoadFrom(this->assemblyFile);
注意:在本例中需要.NET類庫項目引用 PDF.NET SOD框架,在項目的“管理Nuget程序包”里面搜索 PDF.NET.SOD.Core 添加此引用即可。
學(xué)會了這些C++的基礎(chǔ)語法,那么編寫C++/CLI代碼就沒有主要的障礙了。
在C++/CLI中使用反射
反射調(diào)用第一個.NET類的方法
下面的方法,將會反射調(diào)用 User類的一個最簡單的方法 :
public int GetUserID(string IdString){}
該方法只有一個一個參數(shù)和一個簡單的返回值,下面是C++/CLI的反射調(diào)用代碼:
int GetUserID(String^ iDstring)
{
MethodInfo^ method = this->dotnetObject->GetType()->GetMethod("GetUserID", BindingFlags::Public | BindingFlags::Instance);
Func<String^, int>^ fun = (Func<String^, int>^)Delegate::CreateDelegate(Func<String^, int>::typeid, this->dotnetObject, method);
int result = fun(iDstring);
return result;
}
注意這里創(chuàng)建了一個 Func<String,int>的委托方法,使用委托能夠簡化我們的反射調(diào)用并且有時候還能夠提高效率,在這段代碼中,有1個要注意的地方:
Func<String^, int>::typeid
這是C++/CLI特殊的語法,表示獲取“句柄”類型的類型ID,實際上它的結(jié)果就Type對象,等同于C#的
typeof(Func<string,int>)
PS:非常遺憾的是,typeid方式,沒法得到下面類型的類型值:
typeof(Func<,>),這給我們在動態(tài)構(gòu)造泛型對象的時候造成了很大的困惑。
再看一個簡單方法的反射:
DateTime GetUserBirthday(int userId)
{
MethodInfo^ method = dotnetObject->GetType()->GetMethod("GetUserBirthday", BindingFlags::Public | BindingFlags::Instance);
Func<int, DateTime>^ fun = (Func<int, DateTime>^)Delegate::CreateDelegate(Func<int, DateTime>::typeid, this->dotnetObject, method);
DateTime result = fun(userId);
return result;
}
注意:由于DateTime是值類型,因此在進行類型申明的時候,不需要加^符號,僅需要對Func委托加上^句柄標記。
有了這2個簡單的方法,我們來看看如何調(diào)用這個.NET方法“代理類”:
NetLibProxy::UserProxy^ proxy = gcnew NetLibProxy::UserProxy("..\\NetLib\\bin\\Debug\\NetLib.dll");
int result= proxy->GetUserID("123456");
DateTime date = proxy->GetUserBirthday(result);
System::Console::WriteLine("C++/CLI .Net Proxy Class Call Test Result:\r\n UserID={0},\r\n Birthday={1}",
result,date.ToShortDateString());
OK,第一個C++/CLI代碼調(diào)用成功,而且還是反射調(diào)用的,心情小激動一下。
有關(guān)C++/CLI的反射,委托的詳細資料,可以參考MSDN的介紹:
https://msdn.microsoft.com/zh-cn/library/2x8kf7zx.aspx 使用 C++ 互操作(隱式 PInvoke)
https://msdn.microsoft.com/zh-CN/library/213x8e7w.aspx 泛型委托
在下一篇,我們將繼續(xù)探究C++/CLI 反射調(diào)用.NET中可能遇到"深坑",因此僅打算吧本篇文章作為一個“入門”,免得大家心生恐懼,錯過了挑戰(zhàn)艱險的機會。
以上所述是小編給大家介紹的在C++中反射調(diào)用.NET的方法(一),希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會及時回復(fù)大家的!
您可能感興趣的文章
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對數(shù)怎么表達
- 04-02c語言沒有round函數(shù) round c語言
- 04-02C語言中怎么打出三角函數(shù) c語言中怎么打出三角函數(shù)的值
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10APUE筆記之:進程環(huán)境詳解
- 01-10c++中inline的用法分析
- 01-10如何尋找數(shù)組中的第二大數(shù)


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結(jié)
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 04-02jquery與jsp,用jquery
- 01-10delphi制作wav文件的方法
- 08-05織夢dedecms什么時候用欄目交叉功能?


