小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

C#中使用OpenGL:(二)C#調(diào)用C/C++的dll

 羊玉wngbx 2019-07-10

在C#中使用OpenGL圖形庫為業(yè)余的圖形編程人員提供了很大的便利,可是官方并沒有向用戶提供C#版本的OpenGL圖形接口,在民間有好一些人開發(fā)了C#版的OpenGL接口,使之能夠在C#中使用。這些第三方的C#版OpenGL應(yīng)該說用起來還是不錯(cuò)的,如果說有什么缺點(diǎn)的話,那應(yīng)該是這些OpenGL的版本都不是最新的,一般在4.0以下,而現(xiàn)在OpenGL都4.6版本了。如果要使用最新的OpenGL圖形接口,那還得自己多動(dòng)動(dòng)手。

C#如何利用OpenGL?

官方的C#版本的OpenGL接口是不存在的,要想在C#中使用OpenGL,可有兩種方法:從C語言的OpenGL動(dòng)態(tài)鏈接庫(opengl32.dll)中獲取函數(shù)接口,或者直接從硬件驅(qū)動(dòng)中獲取函數(shù)指針。
如果只利用1.1版本的OpenGL,可以選擇調(diào)用opengl32.dll里面的300多條函數(shù)。opengl32.dll可以在windows系統(tǒng)的系統(tǒng)盤里找到,其中的函數(shù)都是調(diào)用約定為stdcall的C函數(shù);
如果要使用高版本的OpenGL,則需要到硬件驅(qū)動(dòng)里獲取函數(shù)指針。windows系統(tǒng)只支持1.1版本的OpenGL,而1.2之后的版本都不再支持,這意味著我們不能通過opengl32.dll這個(gè)庫去調(diào)用新版本的函數(shù),但是可以利用opengl32.dll中的wglGetProcAddress函數(shù)從顯卡驅(qū)動(dòng)中獲取OpenGL函數(shù)指針,然后通過函數(shù)指針來調(diào)用相應(yīng)的函數(shù)。
不管怎么樣,要想在C#中使用OpenGL,調(diào)用opengl32.dll這個(gè)C語言動(dòng)態(tài)鏈接庫是在所難免的。因此特地花一些時(shí)間來研究C#如何調(diào)用C/C++函數(shù)。

C#如何調(diào)用C/C++的dll?

C#調(diào)用dll的方法一般由兩種,分別是靜態(tài)調(diào)用和動(dòng)態(tài)調(diào)用。

C#靜態(tài)調(diào)用dll的方法:

首先,引用名稱空間System.Runtime.InteropServices。
其次,要聲明一個(gè)外部的方法,其基本形式如下:

[DLLImport("XXX.dll")]修飾符 extern 返回變量類型 方法名稱 (參數(shù)列表)

其中:
**1.DLLImport:**這是必不可少的,而且必須要有中括號(hào)“[]”括起來,它有若干可選的參數(shù),叫DllImportAttribute。它至少有一個(gè)參數(shù),這個(gè)必需的參數(shù)是dll的文件名。它也可以有多個(gè)參數(shù),這些參數(shù)可以全部寫,也可以只寫一部分,要看具體情況。這些參數(shù)分別是:

*CharSet 指示用在入口點(diǎn)中的字符集,如:CharSet=CharSet.Ansi;
*SetLastError 指示方法是否保留 Win32"上一錯(cuò)誤"SetLastError=true;
*ExactSpelling 指示 EntryPoint 是否必須與指示的入口點(diǎn)的拼寫完全匹配,如:ExactSpelling=false;
*PreserveSig指示方法的簽名應(yīng)當(dāng)被保留還是被轉(zhuǎn)換, 如:PreserveSig=true;
*CallingConvention指示入口點(diǎn)的調(diào)用約定, 如:CallingConvention=CallingConvention.Cdec。
*EntryPoint指明入口點(diǎn),必須是dll中實(shí)際的函數(shù)名,該項(xiàng)不寫的話,那么方法名稱必須要與dll中的函數(shù)名稱一致。

**2.修飾符:**訪問修飾符,除了abstract以外,聲明方法時(shí)可以使用的任意一種修飾符。如static ,private,public等。一般情況下是public+static一起使用。
**3.返回變量類型:**在DLL文件中你需調(diào)用方法的返回變量類型。
**4.方法名稱:**在DLL文件中你需調(diào)用方法的名稱。
**5.參數(shù)列表:**在DLL文件中你需調(diào)用方法的列表。

例子:

//引用名稱空間
System.Runtime.InteropServices;

//導(dǎo)入外部函數(shù)
[DllImport("opengl32.dll",ExactSpelling =false,EntryPoint = "glBegin",CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern  void glBegin(uint mode);

//指明了函數(shù)入口點(diǎn),則方法名可以與函數(shù)名不一致。
[DllImport("opengl32.dll",ExactSpelling =false,EntryPoint = "glEnd",CharSet = CharSet.Auto,CallingConvention = CallingConvention.StdCall)]
public static extern  void End();

注意事項(xiàng):
1.常見的錯(cuò)誤是沒有引用名稱空間System.Runtime.InteropServices。
2.來自外部的函數(shù),修飾符中必須要有static修飾。
3.調(diào)用約定必須要與dll文件中函數(shù)的調(diào)用約定一致,VC/VS一般默認(rèn)的調(diào)用約定為Cdec,C#中默認(rèn)的調(diào)用約定也為Cdec,如果dll文件的函數(shù)使用的是其他的調(diào)用約定,則C#中必須指明。OpenGL函數(shù)的調(diào)用約定是StdCall,因此CallingConvention = CallingConvention.StdCall。
4.C#可以順利地調(diào)用C語言的dll,一般只要調(diào)用約定和入口點(diǎn)寫得正確,調(diào)用是沒有問題的。但如果dll是由C++生成的,那么就略顯麻煩。由于C++的函數(shù)編譯為dll時(shí),函數(shù)名稱會(huì)被改變,導(dǎo)致在C#中使用時(shí)會(huì)出現(xiàn)找不到入口點(diǎn)。解決辦法是,使用depends工具查看dll中函數(shù)的名稱,然后在C#將EntryPoint指定為該函數(shù)在dll中的名稱。比如Add函數(shù),用C++編譯為dll時(shí),它在dll中的名稱是?Add@@YAHHH@Z,而不再是Add,因此必須設(shè)定EntryPoint = “?Add@@YAHHH@Z”。

C#動(dòng)態(tài)調(diào)用dll的方法:

動(dòng)態(tài)調(diào)用的好處就是需要時(shí)裝載,不需要時(shí)可以釋放。動(dòng)態(tài)調(diào)用C的dll需要用到兩個(gè)winAPI,分別是LoadLibrary和GetPrcAddress。它們的功能分別是將dll文件載入到內(nèi)存和從內(nèi)存中的dll中獲取函數(shù)指針。除了要用到winAPI之外,還要用到C#的委托。使用winAPI獲取函數(shù)指針,然后用委托來執(zhí)行函數(shù)。下面通過示例代碼來說明。

(1)首先聲明外部函數(shù)

//參數(shù)為string類型,字符集應(yīng)設(shè)為CharSet = CharSet.Ansi。
[DllImport("kernel32.dll",ExactSpelling =false,EntryPoint = "Loadlibrary",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static IntPtr Loadlibrary(string fileName);

[DllImport("kernel32.dll",ExactSpelling =false,EntryPoint = "GetProcAddress",CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static IntPtr GetProcAddress(IntPtr hmodule,string functionName);

(2)聲明委托(以glBegin函數(shù)為例)

internal delegate void FUNC(uint mode);

(3)動(dòng)態(tài)調(diào)用opengl32.dll中的glBegin函數(shù)
聲明了外部函數(shù)和委托之后,就可以動(dòng)態(tài)調(diào)用dll了。

//載入opengl32.dll到內(nèi)存
IntPtr hmodule=LoadLibrary("opengl32.dll");
//獲得opengl32.dll中的glBegin函數(shù)指針
IntPtr funcPointer=GetProcAddress(hmodule,"glBegin");
//將函數(shù)指針轉(zhuǎn)換為委托
FUNC glBegin=Marshal.GetDelegateForFunctionPointer(funcPointer,typeof(FUNC));
//執(zhí)行函數(shù)
glBegin(mode);

C#調(diào)用C/C++函數(shù)時(shí)的參數(shù)傳遞問題

下表是C#與C/C++的參數(shù)類型的對(duì)應(yīng)關(guān)系

C#C/C++OpenGL自定義類型
byteunsigned charGLubyte/GLboolean
sbytecharGLchar/GLbyte
shortshortGLshort
ushort、charunsigned shortGLushort
intint/longGLint/GLsizei
uintunsigned int/unsigend longGLuint/GLemun/GLbitfield/GLulong
longlong longGLint64
ulongunsigned long longGLuint64
floatflaotGLfloat/GLclampf
doubledoubleGLdouble/GLclampd
decimal
boolboolGlboolean
byte[]unsigned char*、unsigned char[]GLuchar*
sbyte[]char*/char[]GLchar*
數(shù)組數(shù)組/內(nèi)存塊指針指針
結(jié)構(gòu)體結(jié)構(gòu)體結(jié)構(gòu)體
ref和out修飾的參數(shù)變量指針變量的指針
IntPtr對(duì)象句柄、指針指針

由于C#一般不用指針,C#中的函數(shù)參數(shù)傳遞遇到指針,可用數(shù)組替代,也可以用IntPtr替代。若函數(shù)返回值是指針,則不能用用數(shù)組接收,而要用C#的IntPtr類型的變量接收。IntPtr類型是用來代表指針或句柄的平臺(tái)特定類型,實(shí)際上就相當(dāng)于C語言的指針。
當(dāng)然,對(duì)于一個(gè)熟練地使用C語言的程序員來說,不用指針就好像缺了什么東西一樣。實(shí)際上,指針真的很好用,微軟也是知道的,于是給C#留了一個(gè)后門。特殊情況下,C#也是可以像C語言一樣使用指針。只要給使用指針的代碼塊用關(guān)鍵字unsafe標(biāo)識(shí),并且在工程中菜單欄“項(xiàng)目->屬性”頁面中勾選“允許不安全代碼”,就可使用指針了。具體操作,可以參考百度經(jīng)驗(yàn):C#使用指針(不安全代碼)

例子:

C語言中的函數(shù)定義

#include<stdlib.h>
//結(jié)構(gòu)體
typedef struct MyStruct
{
	int x;
	int y;
}MyStruct;

//函數(shù)FUNC1,參數(shù)是數(shù)組,返回值是指針
_declspec(dllexport)int * FUNC1(int A[])
{
	int n;
	n = sizeof(A);
	int *B = (int*)malloc(n * sizeof(int));
	for (int i = 0;i < n;i++)
	{
		B[i] = A[i] + 1;
	}
	return B;
}
//函數(shù)FUNC2,參數(shù)有兩個(gè)指針
_declspec(dllexport)void FUNC2(int *A,int n,int *B)
{
	for (int i = 0;i < n;i++)
	{
		B[i] = A[i] + 1;
	}
}
//函數(shù)FUNC3,參數(shù)是兩個(gè)數(shù)組
_declspec(dllexport)void FUNC3(int A[],int B[])
{
	int n;
	n = sizeof(A);
	for (int i = 0;i < n;i++)
	{
		B[i] = A[i] + 1;
	}
}
//函數(shù)FYNC4,參數(shù)和返回值都是結(jié)構(gòu)體
_declspec(dllexport)MyStruct FUNC4(MyStruct s)
{
	MyStruct *B = (MyStruct*)malloc(sizeof(MyStruct));
	(*B).x = s.x + 1;
	(*B).y = s.y + 1;
	return *B;
}

C#不使用指針調(diào)用和C語言的函數(shù):

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC1", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr FUNC1(Int32[] a);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC2", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void FUNC2(Int32[] a, Int32 n, Int32[] b);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC3", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void FUNC3(Int32[] a, Int32[] b);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC4", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
 public static extern MYSTRUCT FUNC4(MYSTRUCT S);

C#使用指針調(diào)用C語言的函數(shù):

unsafe{
[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC1", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern int* FUNC1(int[] a);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC2", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void FUNC2(int* a, Int32 n,int* b);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC3", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public static extern void FUNC3(Int32[] a, Int32[] b);

[DllImport("mydll.dll", ExactSpelling = false, EntryPoint = "FUNC4", CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
 public static extern MYSTRUCT FUNC4(MYSTRUCT S);
}

結(jié)語

本文也沒有真正進(jìn)入“開發(fā)C#版OpenGLj接口”這個(gè)主題中,只是一個(gè)前期的技術(shù)儲(chǔ)備。后期開發(fā)C#版的OpenGL接口,必然會(huì)用到以上這些知識(shí)。俗話說,磨刀不誤砍柴工,有好的準(zhǔn)備能很好地進(jìn)入主題,所以在真正大刀闊斧開始干的時(shí)候,會(huì)花上一段時(shí)間進(jìn)行技術(shù)儲(chǔ)備。下一篇文章,將介紹如何將一個(gè).lib文件直接編譯為.dll文件。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多