C# 調用C/C++動態鏈接庫,結構體中的char*類型


用C#掉用C++的dll直接import就可以之前有不同的類型對應,當要傳遞結構體的時候就有點麻煩了,這里有一個結構體里邊有char*類型,這個類型在C#中調用沒法聲明,傳string是不行的默認string是對應const char*,傳stringbuilder得指定大小,不然是沒法傳的,

查了好久,最后只能用unsafe代碼來實現了

用C/C++寫一個標准的動態鏈接庫:

頭文件,定義了三個接口函數,

#pragma once
#define  TESTDLL _declspec(dllexport)

#ifndef PKI_DATA_ST
#define PKI_DATA_ST
typedef  struct PKI_DATA_st {
    int size;
    char *value;
}PKI_DATA, *PKI_PDATA;
#endif


extern "C"
{
    TESTDLL  double  Add(double a, double b);

    TESTDLL  double  Subtract(double a, double b);

    TESTDLL int TestDn(PKI_DATA cert, PKI_DATA* dn);
    
}

簡單實現:

#include "stdafx.h"
#include "TestClass.h"



 double  Add(double a, double b)
{
     printf("Add \n");
    return a + b;
}

 double  Subtract(double a, double b)
 {
     printf("Subtract");
     return a - b;
 }


 int TestDn(PKI_DATA cert, PKI_DATA* dn)
 {
     printf("TestDn  cert.value: %s\n", cert.value);
     dn->size = 10;
     //dn->value =  "helloworld";     //這個種寫法需要轉換
     dn->value =(char*) TEXT("helloworld");//這種寫法在選擇unicode字符集時C#是可以直接接受的
     return 1;
 }

C#調用,路徑可以用當前程序的相對路徑,也可以是系統的環境變量中的路徑,聲明import

        [DllImport("USBLoad.dll", EntryPoint = "Add", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        static extern double  Add(double a,double b);

        [DllImport("USBLoad.dll", EntryPoint = "TestDn", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        static extern int TestDn(PKI_DATA cert, ref PKI_DATA dn);

        [DllImport("USBLoad.dll", EntryPoint = "Subtract", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
        static extern double Subtract(double  a, double b);

聲明結構體:

        internal unsafe struct PKI_DATA
        {
            internal int size;
            internal char* value;
        }

調用:記得屬性中勾上允許不安全代碼

 [HandleProcessCorruptedStateExceptions]//這個可以捕獲C++中的異常 
    [SecurityCritical]
static unsafe void TestUSBLoad()//加unsafe { try { var a = Add(10, 10); w(a); a = Subtract(15, 10); w(a); PKI_DATA cert = new PKI_DATA(); cert.size = 4; char[] test = "test".ToCharArray(); string s = ""; fixed (char * v=test)//傳值初始化。需要fixed關鍵字 { cert.value = v; PKI_DATA dn = new PKI_DATA(); TestDn(cert, ref dn); var encode = Encoding.ASCII; var c = dn.value; List<char> list = new List<char>(); for (int i=0;i<dn.size;++i,++c) { list.Add(*c); }
            //兼容ansi編碼,先轉byte
var bytes = Encoding.Unicode.GetBytes(list.ToArray()); s = Encoding.ASCII.GetString(bytes);//轉碼 w(new string(dn.value));//設置unicode字符集的情況下可以直接這么寫就能得到要字符 } s = s.Replace("\0","");//轉碼的時候后邊會有很多\0,移除 w(string.Format("[{0}]",s)); } catch (Exception ex) { w(ex.Message); } }

順便研究了下C++的動態調用、

如果是靜態調用需要頭文件和lib庫,直接引用頭文件,加入lib的鏈接路徑,引用后可以直接用,編譯就可以了

動態調用用loadlibrary,記得freelibrary,這些函數在windows.h中,這里建的是QT的項目所以引用的是qt_windows.h:

#include "qt_windows.h"

#define DLLPATH  (LPCTSTR)"USBLoad.dll"

#ifndef PKI_DATA_ST
#define PKI_DATA_ST
typedef  struct PKI_DATA_st {
    int size;
    char *value;
}PKI_DATA, *PKI_PDATA;
#endif


//聲明入口函數 typedef
double(*Add_Func)(double, double); typedef int(*TestDn)(PKI_DATA, PKI_DATA*); int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "hello world!"; HINSTANCE m_hModule = LoadLibrary(DLLPATH);//加載dll if (m_hModule == NULL) { printf("LoadLibrary %s failed,err %d\n", DLLPATH, GetLastError()); goto END; } Add_Func add; add = (Add_Func)GetProcAddress(m_hModule,"Add");//獲取方法地址,后邊調用 double c = add(10, 11); printf("Add_Func: %lf \n", c); TestDn test; test = (TestDn)GetProcAddress(m_hModule, "TestDn"); PKI_DATA cert; PKI_DATA dn; cert.size = 4; cert.value = (char*)"test"; int res = test(cert, &dn); printf("TestDn:%s\n", dn.value); END: if (m_hModule != NULL) { FreeLibrary(m_hModule); m_hModule = NULL; printf("FreeLibrary\n"); } return a.exec(); }

 關於怎么把char*轉碼到正確的編碼格式下的C#string,

 char* cchar=data   
 IntPtr value = new IntPtr(cchar);
 var s = Marshal.PtrToStringAnsi(value);

可以用上邊的方法,看fcl的源碼可以看到上邊的方法實質是把char*強轉為sbyte*,然后直接new string,這樣可以避免C++和C#的byte差異

marshal還有touni的方法,只轉unicode的方法,實質是直接用char* new string,

char*和sbyte*兩種初始化參數string類型都接受。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM