日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > C# >内容正文

C#

c# Task.Factory.StartNew 传参数_C#/C++混合编程一二事

發布時間:2024/4/11 C# 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 c# Task.Factory.StartNew 传参数_C#/C++混合编程一二事 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

C#/C++混編的情形經常會碰到,下面就來講一講一些需要注意的點。廢話不多說,Let's get started. (時間有限,暫時沒有寫完,后續會持續更細。如果有寫的不嚴謹甚至錯誤的地方歡迎大家指正)

一、C++導出函數的聲明

在導出函數的頭文件,經常見到如下的模板

#pragma once

下面就來給出解釋。

  • #ifdef __cplusplus這條預編譯指令。如果項目是C++的項目,則用extern "C" {} 將所有的代碼包起來。這樣的好處是導出的函數名仍和代碼中定義的一致,否則函數名前后會加上一些看起來很奇怪的符號。這是因為C++的函數有重載機制,同一個函數名,可以有多種參數形式,不加extern "C"包起來的話,編譯器就會在函數名前后加上一些符號,來具體到這個函數的具體的某一種形式。
  • #ifdef YOURPROJECT_EXPORTS這條預編譯指令。這段話的意思就是如果定義了YOURPROJECT_EXPORTS這個宏,則把YOURPROJECT_API這個宏定義成__declspec(dllexport),否則把YOURPROJECT_API定義成__declspec(dllimport)。這個設計是為了開發者和調用者的便利性設計的。因為這個頭文件,后來也會拷貝給調用者用,開發者的導出函數的這個項目中定義一個宏YOURPROJECT_EXPORTS,而調用者那邊沒有這個宏。所以YOURPROJECT_API對于開發者就是__declspec(dllexport),對于調用者就是__declspec(dllimport)。__declspec(dllexport)告訴編譯器這是一個需要導出的函數,__declspec(dllimport)告訴編譯器這是一個從外部的庫文件中導入的函數。
  • #define CALLINGCONVENTION _cdecl 這個宏,就是調用約定,可以根據需要把它定義成__cdecl或者__stdcall或者其他。調用約定關系到函數參數入棧和出棧的清理方式。常見的有__cdecl、_stdcall、_fastcall. 最常用的就是_cdecl、_stdcall. 如果調用方用C#,則推薦用_cdecl. 因為_stdcall也會像沒加extern "C" 那樣導致導出的函數名和頭文件中定義的不一致。(__cdecl許多人記不住,其實它就是C Declaration的縮寫)

二、C++導出函數的實現

聲明寫好了,實現就簡單了。參考如下,不解釋。

#include

三、C#導入函數的聲明

C#調用之前,先寫一個類,里面用來聲明從C++導入的函數

class

說明:

  • 記得添加命名空間using System.Runtime.InteropServices;
  • 函數聲明前寫上DllImport屬性,包含導入的庫文件名,調用約定,字符集,入口點。
  • 庫文件名可以寫相對路徑,也可以寫絕對路徑。
  • 調用約定和C++保持一致,推薦用Cdecl方式,如果強行用Stdcall會直接報錯。
  • 字符集可寫可不寫,一般不會用上。但是如果C++參數中有char*或者wchart*時,就需要根據實際情況填寫字符集這個屬性。如果參數為wchar_t*時,指定字符集為Unicode, C#的參數使用string即可(當然也可以使用StringBuilder,具體不討論)。當然也可以不指定字符集,在參數前加上如下聲明[MarshalAs(UnmanagedType.LPWStr)]也可。
  • 入口點,就是dll中導出的函數的符號,當且僅當調用約定為__cdecl、并且加了extern "C"包裝的情況下,入口點才和函數名相同。

四、參數的傳遞

  • 基本類型,比如int、float、double這些直接傳就好,不再贅述。
  • 字符串類型。舉個例子:
//C++:
  • 一般指針,比如void*、自定義類的指針YourClass*、句柄如HANDLE、HWND等。舉個例子
//C++: typedef void* IDicomSCU; YOURPROJECT_API void CALLING_CONVENTION IDicomSCU_Close(IDicomSCU handle);//C#: [DllImport(strDllName, CallingConvention = ccDef, CharSet = csDef, EntryPoint = "IDicomSCU_Close")] public static extern void IDicomSCU_Close(IntPtr handle); //說明:既然這么多種東西都可以用C#的IntPtr來傳遞,那在編碼時一定需要注意IntPtr具體指向的一個什么東西,切不可混用。
  • 函數指針。這種情形,C++需要一個函數指針,C#傳委托即可。舉個例子
//C++ typedef bool(*IDriverCommandEvent)(int command, wchar_t *val); YOURPROJECT_API bool CALLING_CONVENTION IDriver_Open(HWND hWnd, IDriverCommandEvent event);//C# //先聲明一個委托,注意委托的參數中有string類型,我就在前方加上了[MarshalAs(UnmanagedType.LPWStr)]以和C++的wchar_t*對應 //并且聲明這個委托的調用約定是Cdecl還是Stdcall,這個很重要,必須和C++端的調用約定保持一致。 [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] public delegate bool DriverCommandEventHandler(int command, [MarshalAs(UnmanagedType.LPWStr)] string val); //導出函數聲明 [DllImport(strDllName, CallingConvention = ccDef, CharSet = csDef, EntryPoint = "IDriver_Open")] [return: MarshalAs(UnmanagedType.I1)] public static extern bool IDriver_Open(HWND hWnd, DriverCommandEventHandler driverCommandEvent); //說明:注意C#在傳遞形參的時候,形參不要是一個臨時對象,因為臨時對象的引用計數為0后會被GC回收, //導致C++調用這個函數指針的時候找不到而報錯
  • 結構體指針。
  • 參照C++端定義的結構體也在C#端定義同樣的結構體,具體參見第六節。然后C#的形參用ref YourStruct類型,C++端用YourStruct*接收。

五、返回值的傳遞

基本類型基本可以直接聲明,這里就把特殊的返回值拿出來提一下

  • bool類型:如果按照常規的寫法,返回值總是錯誤的。我搜索了stackoverflow,發現了這個問題的解決方案,參考鏈接如下:
C# DllImport with C++ boolean function not returning correctly?stackoverflow.com

因此,聲明就應該這樣寫:

YOURPROJECT_API [DllImport(strDllName, CallingConvention = ccDef, CharSet = csDef, EntryPoint ="DoSomething")]

六、復雜的數據類型的定義(結構體)

總結

以上是生活随笔為你收集整理的c# Task.Factory.StartNew 传参数_C#/C++混合编程一二事的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。