根據(jù)我的猜測(cè),有兩種情況
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過有效、簡(jiǎn)單的方式提供給客戶,將通過不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:域名注冊(cè)、網(wǎng)頁空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、英山網(wǎng)站維護(hù)、網(wǎng)站推廣。
1.如果?void?GetNum(char*?s);是把輸入的字符儲(chǔ)存在?*s中的話:
#include?WINDOWS.H????
#include?WINBASE.H
#include?stdio.h????
typedef?void?(*MYPROC)(char*);?/*?這里要與GetNum的返回值和參數(shù)?對(duì)應(yīng)?*/
void?main(){
HINSTANCE?LibHandle;
MYPROC?ProcAdd;?
char?ch;????
char?sysbuf[]?=?"GetNum";???/*過程名*/
LibHandle?=?LoadLibrary("GetNum.dll");?/*載入dll*/?
ProcAdd?=?(MYPROC)GetProcAddress(???
LibHandle,?sysbuf);???/*獲取函數(shù)sysbuf的地址*/
ProcAdd(ch);????
printf("%c\n",ch);
}
2.如果你的GetNum是
int?GetNum(char*?s)
{
int?i=0,ans=0;
while(s[i]='0's[i]='9')
{
ans=s[i]-'0'+ans*10;
}
return?ans;
}
#include?WINDOWS.H????
#include?WINBASE.H
#include?stdio.h????
typedef?int??(*MYPROC)(char*);???????????/*???改?*/
void?main(){
HINSTANCE?LibHandle;
MYPROC?ProcAdd;?
char?s[100];????????????????????????/*???改?*/
char?sysbuf[]?=?"GetNum";???/*過程名*/
LibHandle?=?LoadLibrary("GetNum.dll");?/*載入dll*/?
ProcAdd?=?(MYPROC)GetProcAddress(???
LibHandle,?sysbuf);???/*獲取函數(shù)sysbuf的地址*/
scanf("%s",s);????????????????????/*???改?*/
printf("%d\n",?ProcAdd(s)?);????????/*?這里調(diào)用?并輸出結(jié)果?*/
}
(1)編寫程序時(shí),你要包含(#include "什么.h") dll文件作者提供 的 頭文件(.h文件) 。
程序里,便和普通函數(shù)調(diào)用一樣,去調(diào)用它的函數(shù)。
(2)程序編譯時(shí),你要鏈接 dll文件作者提供 的 (.lib文件) 庫文件。
當(dāng)然,你可以在源程序里把.lib 名字 寫上,編譯時(shí)自動(dòng)去鏈接,例子:
#pragma comment (lib, "什么.lib")
(3)執(zhí)行時(shí),要有 .dll 文件. 放在當(dāng)前文件夾或系統(tǒng)文件夾里。
其實(shí)在.net開發(fā)中確實(shí)存在第三種類型——指針。但僅限于unsafe開發(fā)時(shí)使用!
對(duì)于dll調(diào)用問題,如果是pe格式的dll考慮使用PInvoke(平臺(tái)調(diào)用)。而平臺(tái)調(diào)用跟具體的unsafe開發(fā)是沒有任何關(guān)聯(lián)的,并不是說平臺(tái)調(diào)用一定會(huì)用到非安全開發(fā)!由于.net本身隱藏了指針的使用,你不必要再去開辟任何指針了!所以這里顯然是平臺(tái)調(diào)用。
解答一個(gè)疑問:數(shù)組指針or指針數(shù)組?
數(shù)組指針指的一個(gè)指針,該指針指向一個(gè)數(shù)組。
指針數(shù)組是一個(gè)數(shù)組,一個(gè)數(shù)組里全是指針。
顯然這使用是有區(qū)別,但這里并不涉及,在safe開發(fā).net都不會(huì)涉及!
IntPtr是一種特殊的指針,這種針指的特殊性在于它向了設(shè)備!所以嘛,他也有一個(gè)名詞——名柄,IntPtr多種在PInvoke時(shí),傳遞設(shè)備句柄時(shí)使用(設(shè)備并不是硬件,有時(shí)一個(gè)文件也算是一個(gè)句柄的)。當(dāng)然,這里也用不到!
現(xiàn)在說一下平臺(tái)調(diào)用(PInvoke)中最重要的一個(gè)部分!數(shù)據(jù)封送!
也就是你寫dll導(dǎo)入時(shí)應(yīng)用使用的類型!
在C語言中聲明與賦值分開寫的時(shí)候多了去了!他生成文件時(shí)寫一起與分開寫并沒有任何區(qū)別!所以分開寫無所謂的!但是,多數(shù)時(shí)間你會(huì)發(fā)現(xiàn),其實(shí)他分開寫是為了你閱讀的方便!為什么?因?yàn)槁暶鲿r(shí)使用的類型是PUSHORT,而賦值時(shí)使用了USHORT的類型,這兩句寫在一塊的話,很多人不會(huì)注意到賦值時(shí)使用的基礎(chǔ)類型!所以分開寫只是相當(dāng)于想寫一個(gè)注釋而已!
平臺(tái)對(duì)應(yīng)的數(shù)據(jù)封送就在這里!USHORT在C/CPP中對(duì)就的一個(gè)byte!一定要注意,所以你知道的,他們使用的new USHORT[n]只是開辟了n個(gè)無符號(hào)數(shù)的字節(jié)!在.net平臺(tái)封送時(shí)如何傳遞數(shù)據(jù)?當(dāng)然用byte去接值,他是數(shù)組,你就用byte[]去接!
這里有一個(gè)問題你可能不知道,在.net中,最小的數(shù)是byte,而不是short和ushort!在.net中可以直接使用byte與其他數(shù)值進(jìn)行運(yùn)算!而short/ushort在.net中卻是一個(gè)16bit數(shù)!占用兩個(gè)字節(jié)!而C/Cpp為了確定類型,所以byte是byte,short/ushort是數(shù)值,但是short/ushort卻是一個(gè)8bit數(shù)字!當(dāng)然了,C/Cpp中的long才與.net中的int對(duì)應(yīng)!
如果你明白這個(gè),那么該用int[]還是該用short[]還是該用byte[]你自己就知道了!
第二個(gè)問題——你一直在想指針,其實(shí)你要傳入的數(shù)組是地址而不是副本!換句話來說,在安全模式中我們是有ref/out修飾的!在dllImport時(shí)你傳入byte[]是沒有任何錯(cuò)誤的!但是你能不能在從dll中將byte[]修改后的值帶回就是兩個(gè)字了!
一般情況下byte[]等數(shù)組本身就是引址的方式,所以不使用ref/out聲明也是沒有問題的!但是如果封裝層在有一定的疑問存在時(shí),最好還是聲明成ref的方式較為合適。經(jīng)常性的規(guī)律是,如果我們看到Cpp的header中說明是指針時(shí),我們會(huì)使用ref聲明,不是指針形式時(shí)可以不用ref/out聲明,如果是**形式時(shí),平臺(tái)封送最好使用數(shù)組指針。這種情況并不多見!
但是,由于string類型也是引用形的,由于其特點(diǎn),返回值一定要用ref/out(不管你看到Cpp的header中是什么類型),或者我們可以使用一個(gè)說明長(zhǎng)度的byte[]也是可行的!
換句話來說,雖然我們可以說C#中的byte可以接short, short可以接int等等的對(duì)應(yīng)關(guān)系!但其實(shí)在不改變內(nèi)存字節(jié)長(zhǎng)度的情況下,PInvoke對(duì)類型的要求并沒有那么嚴(yán)格!比如你可以用string接收dll中傳入的字符串,用byte[]也行的!用stringbuilder還行的!并沒有嚴(yán)格上的限制!但是這里邊一定要注意的是長(zhǎng)度!換句話來說,對(duì)方使用的是USHORT[n],你用byte[n]能接回來。
是不是一定要有這個(gè)對(duì)應(yīng)關(guān)系?不一定!
這種情況往往出來在自定類中。比如Cpp header中說明某個(gè)參數(shù)一個(gè)結(jié)構(gòu)!這個(gè)結(jié)構(gòu)是由一個(gè)short,一個(gè)字符串組成的!用C#時(shí)可以使用byte實(shí)現(xiàn)結(jié)構(gòu)中對(duì)應(yīng)的short,也可以用short實(shí)現(xiàn)對(duì)應(yīng)的short,但在聲明必須使用特定的長(zhǎng)度說明,說明其只有一位!當(dāng)然用,無論你用int還是long都可以,在結(jié)構(gòu)中必須要說明長(zhǎng)度只有一位!那個(gè)字符串也是一樣!所以你完全可以看到,數(shù)據(jù)類型并不重要,平臺(tái)封送中不用管具體的類型,要的只是長(zhǎng)度!長(zhǎng)度必須對(duì)應(yīng)!
換句話來說,你用char[]來處理Cpp中的ushort也是可以的!
平臺(tái)數(shù)據(jù)封送中重要一部分就是共長(zhǎng)度(類型?開什么玩笑,Cpp不會(huì)認(rèn)識(shí).net中的類型的,當(dāng)然.net也不會(huì)認(rèn)識(shí)Cpp中的類型)。只有長(zhǎng)度一致才能接到值!
cpp中肯定是哪種類型方便用哪種了,當(dāng)然.net中也是哪種方便用哪種了!那么平臺(tái)數(shù)據(jù)是如何進(jìn)行傳遞的呢?答案就在于基本類型和結(jié)構(gòu)!
基本類型不說了,當(dāng)然是ushort只是一個(gè)byte,你用int無用謂!反正在.net中byte類型會(huì)自然轉(zhuǎn)換成int。如果是ushort[],那么你必須是byte[]或char[]!你用int[]絕對(duì)會(huì)出現(xiàn)錯(cuò)誤的!為了幫助理解,我這里說的是重點(diǎn)是結(jié)構(gòu):
假定一個(gè)結(jié)構(gòu)有n個(gè)項(xiàng)(你可以理解為字段)組成。那么在數(shù)據(jù)封送時(shí)就是這n個(gè)項(xiàng)每一段的組合。經(jīng)如有字段是USHORT類型,一個(gè)字段是Byte[10]類型,還有一個(gè)是Int類型,在平臺(tái)封送時(shí),我們要封裝一段內(nèi)存,長(zhǎng)度為13,第一個(gè)長(zhǎng)度為1表示第一字段,接下來10個(gè)表示第二字段,接下來2個(gè)表示 第三字段。
當(dāng)然我們?cè)?net方面也要構(gòu)造一個(gè)對(duì)應(yīng)的結(jié)構(gòu)!第一個(gè)字段你可以聲明成byte/short/int/long都可以(必須說明這個(gè)在內(nèi)存中只有一個(gè)字節(jié)),如果你用了short卻不說明,很顯然你.net會(huì)接到前兩字節(jié),破壞了要接收的結(jié)構(gòu)!你自己肯定拿不到數(shù)據(jù)的!
原理就這么多!慢慢看懂了就知道平臺(tái)封送是怎么回事了!至于你的問題,抄個(gè)近路告訴你結(jié)果:dllImport聲明時(shí)使用
static bool Read(short h, byte[] buffer, short n);
或者
static bool Read(short h, ref byte[] buffer, short n);
當(dāng)然,也可以使用int,我并不反對(duì)
static bool Read([MarshalAs(UnManagerType.USHORT)] int h, byte[] buffer, short n);
換句話來說,你可以使用MarshalAsAttribute來指針其非托類型,結(jié)構(gòu)時(shí)還可以直接使用長(zhǎng)度說明。主要是你學(xué)會(huì)MarshalAsAttribute的使用,就知道參數(shù)如何傳遞了!
一.動(dòng)態(tài)鏈接庫(dll)結(jié)構(gòu)
——dll中定義有兩種函數(shù):導(dǎo)出函數(shù)(export
function)和內(nèi)部函數(shù)
(internal
function),導(dǎo)出函數(shù)可以被其他模塊調(diào)用,內(nèi)部函數(shù)只能在dll內(nèi)部使用。我們?cè)谟胏++定制dll文件時(shí),需要編寫的就是包含導(dǎo)出函數(shù)表的模塊定義文件(.def)和實(shí)現(xiàn)導(dǎo)出函數(shù)功能的c++文件。下面以sample.dll為例介紹def文件和實(shí)現(xiàn)文件的結(jié)構(gòu):
——1.模塊定義文件(.def)是由一個(gè)或者多個(gè)用于描述dll屬性的模塊語
句組成的文本文件,每個(gè).def文件至少必須包含以下模塊定義語句:
第一個(gè)語句必須是library語句,指出dll的名字。
exports語句列出被導(dǎo)出函數(shù)的名字。
可以使用description語句描述dll的用途(此句可選)。
";"對(duì)一行進(jìn)行注釋(可選)
——2.實(shí)現(xiàn)文件(.cpp文件為例)
——實(shí)現(xiàn)入口表函數(shù)的.cpp文件中,包含dll入口點(diǎn)處理的api函數(shù)和導(dǎo)出
函數(shù)的代碼。
//dll
#include?windows.h
extern?"C"?//保持C語言文件
void?_declspec?(?dllexport?)?tryProc()???????//定義函數(shù)
{
MessageBox(NULL,_T("a"),_T("a"),MB_OK);
}
-------------------------------------
//C
#include?windows.h
int?main()
{
HMOUDLE?dll?=?LoadLibrary(/*DLL文件名*/);
if(dll?!=?NULL)
{
FARPROC?try?=?GetProcAddress(dll,"tryProc");
if(try?!=?NULL)
{
tryProc();//假如函數(shù)有返回值,可以用try()得到返回值
/*_asm?call?tryProc*/
}
}
return?0;
}