簡體   English   中英

在C和C#之間傳輸控制和數據

[英]Transfer of control and data between C and C#

C#主程序需要調用一個C程序GA。c該C代碼執行許多函數,一個函數initialize()調用Objective()函數。 但是這個目標函數需要用C#編寫。此調用在C代碼中處於循環中,並且在從Objective()返回之后直到其主函數結束並將控制權返回給C#主程序之后,C代碼需要繼續執行。

C# main()
{
  //code

  call to GA in C;
  //remaining code;
}


GA in C:

Ga Main()
{

 //code
 call to initialize function();
 //remaining code
}


initialize function() in GA
{

 for(some condition)
 {
   //code
   call to objective(parameter) function in C#;
   //code
  }
}

我們如何做到這一點?

您的非托管C代碼需要位於庫中,而不是可執行文件中。 當一個程序“調用另一個程序”時,這意味着它執行另一個可執行文件,並且兩個進程之間的任何通信都可以是被調用方的命令行參數形式,以及調用方的整數返回值,或者是某種形式的通信。 IPC * 兩者都不允許傳遞回調函數(盡管可以使用IPC構建等效的功能,但這很麻煩)。

從此C庫中,您需要從C#代碼中導出希望作為入口點的函數。 然后,您可以使用C#中的平台調用來調用此/這些導出的函數。

C庫(MSVC示例):

#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
    switch(ul_reason_for_call){
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}


#ifdef __cplusplus
extern "C"
#endif
__declspec(dllexport)
void WINAPI Foo(int start, int end, void (CALLBACK *callback)(int i)){
    for(int i = start; i <= end; i++)
        callback(i);
}

C#程序:

using System;
using System.Runtime.InteropServices;

static class Program{

    delegate void FooCallback(int i);

    [DllImport(@"C:\Path\To\Unmanaged\C.dll")]
    static extern void Foo(int start, int end, FooCallback callback);

    static void Main(){
        FooCallback callback = i=>Console.WriteLine(i);
        Foo(0, 10, callback);
        GC.KeepAlive(callback); // to keep the GC from collecting the delegate
    }
}

這是工作示例代碼。 根據您的需求進行擴展。


關於P /調用的說明

並不是您所問的,但是有兩種使用平台調用的典型情況:

  1. 利用“舊版”代碼。 這里有兩個很好的用途:

    • 利用您自己的代碼庫中的現有代碼。 例如,您的公司可能希望為其會計軟件使用全新的GUI,但選擇P / Invoke到舊業務層,從而避免了重寫和測試新實現的時間和金錢。

    • 與第三方C代碼接口。 例如,許多.NET應用程序使用P / Invoke訪問未通過BCL公開的本機Windows API功能。

  2. 優化性能關鍵的代碼部分。 在某個例程中發現瓶頸后,開發人員可能會決定使用該例程的本機代碼來嘗試提高速度。

在第二種情況下,通常會出現錯誤判斷。 通常有許多考慮因素證明這是個壞主意:

  1. 通過使用非托管代碼,幾乎沒有明顯的速度優勢。 對於許多開發人員來說,這是一件很難的事,但是編寫良好的托管代碼通常(盡管並非總是如此)的執行速度幾乎與編寫良好的非托管代碼一樣快。 在某些情況下,它可以執行得更快。 如果您有興趣進行搜索,可以在SO上以及網上其他地方對此主題進行一些很好的討論。

  2. 可以使非托管代碼更高效的一些技術也可以在C#中完成。 首先,我在這里指的是C#中的不安全代碼塊,該代碼塊允許人們使用指針,而繞過數組邊界檢查。 此外,直接的C代碼通常以過程方式編寫,從而消除了來自面向對象的代碼的少量開銷。 也可以使用靜態方法和靜態字段以程序方式編寫C#。 雖然通常最好避免使用不安全的代碼和隨意使用靜態成員,但我想說,它們比混合托管和非托管代碼更可取。

  3. 托管代碼是垃圾回收的,而非托管代碼通常不是。 盡管這在編碼時主要是提高速度,但有時在運行時也有提高速度。 當必須管理自己的內存時,通常會涉及一些開銷,例如將一個附加參數傳遞給表示內存塊大小的函數。 還有急切的銷毀和重新分配,這在大多數非托管代碼中都是必需的,而托管代碼可以將這些任務卸載到惰性收集器,在以后可以在懶惰的收集器中執行它們,也許是在CPU不太忙於實際工作時。 從我讀過的內容來看,垃圾回收還意味着分配比在非托管代碼中更快。 最后,在C#中,可以使用Managed.AllocHGlobal和unsafe指針進行一些手動內存管理,這可能使一個較大的分配較少,而不是許多較小的分配。 另一種技術是將大型數組中使用的類型轉換為值類型,而不是引用類型,以便將整個數組的內存分配在一個塊中。

  4. 通常會忽略平台調用層中的成本。 這可能會超過少量的本機代碼性能提升,尤其是當必須發生許多從托管到非托管的轉換時(反之亦然,例如使用回調函數)。 當必須進行封送處理時,此成本會成倍增加。

  5. 在托管組件和非托管組件之間拆分代碼時,存在維護麻煩。 這意味着在兩個不同的項目中維護邏輯,可能使用兩個不同的開發環境,甚至可能需要兩個具有不同技能的不同開發人員。 典型的C#開發人員不是一個好的C開發人員,反之亦然。 至少,以這種方式拆分代碼對於項目的任何新維護人員來說都是一個心理障礙。

  6. 通常,只需重新考慮現有的實現,然后用新的方法重寫它,便可以獲得更好的性能提升。 實際上,我想說的是,為“更快”的平台重寫瓶頸代碼時獲得的大多數實際性能提升可能直接是由於開發人員被迫重新考慮問題。

  7. 有時,選擇丟棄到不受管的代碼並不是真正的瓶頸。 人們常常對使他們減速的原因做出假設,而沒有進行實際的分析來驗證。 分析通常可以揭示效率低下的問題,而這些問題可以很容易地正確地完成, 而無需使用較低級別的平台。

如果您發現自己想要混合使用平台以提高性能,那么請牢記這些陷阱。


*還有一件事。 父進程可以重定向子進程的stdin和stdout流,從而實現通過stdio傳遞基於字符的消息。 這實際上只是一種IPC機制,只是比術語“ IPC”(AFAIK)更長的一種。

這稱為回調。 創建GA實例時,請將其c#objective()方法作為委托傳遞(委托是對類方法的引用)。 在C#中查找有關委托的MSDN幫助主題。

我不知道C端的正確語法。 調出非托管代碼肯定有一些特殊注意事項。 肯定會有其他人提供整個答案。 :)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM