全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:7511
推到 Plurk!
推到 Facebook!

C#引用DLEPHI 的DLL檔,傳回字串結果不同

答題得分者是:aftcast
superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#1 引用回覆 回覆 發表時間:2011-07-13 17:38:19 IP:114.33.xxx.xxx 訂閱
使用DELPHI 寫了一個DLL檔(加解密字串功能),
使用DLEPHI測試可以加密及反解回來。
但用C# 引用時,同樣的文字去加密,
加密後得到的字串卻不相同,
且反解時只能解到前三分之一,其它都為亂碼。

交叉使用(用DELPHI 加密一文字後得到的密文去C#反解) 一樣不行。
是開發環境的問題嗎?

DELPHI 7
VISUAL STUDIO 2008

aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#2 引用回覆 回覆 發表時間:2011-07-14 03:10:13 IP:122.126.xxx.xxx 訂閱
不知你的dll是怎麼寫的,但理論上僅可傳入PChar這類的參數,且記憶體的配置要使用win32 api 裡的,如GlobalAlloc之類的來配,如此配出來的記憶區塊才可以在不同的語言間正常運作。

當然,我個人覺得用bcb寫的dll來給c#用可能更方便一點。不過原理還是一樣同上就是。

以上是我初步的看法,請參考!

===================引 用 superrakce 文 章===================
使用DELPHI 寫了一個DLL檔(加解密字串功能),
使用DLEPHI測試可以加密及反解回來。
但用C# 引用時,同樣的文字去加密,
加密後得到的字串卻不相同,
且反解時只能解到前三分之一,其它都為亂碼。

交叉使用(用DELPHI 加密一文字後得到的密文去C#反解) 一樣不行。
是開發環境的問題嗎?

DELPHI 7
VISUAL STUDIO 2008

------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
rick060
高階會員


發表:2
回覆:112
積分:217
註冊:2009-11-17

發送簡訊給我
#3 引用回覆 回覆 發表時間:2011-07-14 09:31:48 IP:60.250.xxx.xxx 未訂閱

[code c#]
public partial class Form1 : Form
{
[DllImport("Project1.dll")]
public static extern String func_EncryptKey([MarshalAs(UnmanagedType.LPStr)]String src,
[MarshalAs(UnmanagedType.LPStr)]String key);
[DllImport("Project1.dll")]
public static extern String func_DecryptKey([MarshalAs(UnmanagedType.LPStr)]String src,
[MarshalAs(UnmanagedType.LPStr)]String key);

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
String retString;

retString = func_EncryptKey("This is it", "");
MessageBox.Show(retString);

retString = func_DecryptKey(retString,"");
MessageBox.Show(retString);
}
}
[/code]


[code delphi]
function func_EncryptKey(Src: PAnsiChar; Key: PAnsiChar) : PAnsiChar;stdcall;
begin
ShowMessage(Src);
ShowMessage(Key);
Result := PAnsiChar('ReturnString');
end;

function func_DecryptKey(Src: PAnsiChar; Key: PAnsiChar): PAnsiChar;stdcall;
begin
ShowMessage(Src);
ShowMessage(Key);
Result := PAnsiChar('ReturnString');
end;

exports func_EncryptKey;
exports func_DecryptKey;
[/code]

除錯第一要務,單純化,再求問題點
先確定 C# call dll 可以正常傳進傳出 正確的字串,再研究是哪部份的問題
superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#4 引用回覆 回覆 發表時間:2011-07-14 17:48:55 IP:114.33.xxx.xxx 訂閱
您給的範例很有用,但我套入程式碼後便不行
因為要作字串加解碼
function Encrypt(st: PAnsiChar):PAnsiChar;
var
a: array[1..n] of TStrings;
i,j,k,d,ra: integer;
s,temp:string;
begin
s:= st;

然後作一些運算,如:t:= t inttostr(Xmod(Ord(s[i])));
最後result :=PAnsiChar(t);

在c# 調用時發生「嘗試讀取或寫入受保護的記憶體。這通常表示其他記憶體已損毀。」這項錯誤
但如果在result := PAnsiChar(t); 前面先加上showmessage(t); 就不會有問題。
而且不是每次都管用,有時會彈出訊息框完了會正確執行,有時候還是一樣跑出記憶體已損毀的錯誤
這樣是那邊出了問題?
===================引 用 rick060 文 章===================

[code c#]
public partial class Form1 : Form
{
[DllImport("Project1.dll")]
public static extern String func_EncryptKey([MarshalAs(UnmanagedType.LPStr)]String src,
[MarshalAs(UnmanagedType.LPStr)]String key);
[DllImport("Project1.dll")]
public static extern String func_DecryptKey([MarshalAs(UnmanagedType.LPStr)]String src,
[MarshalAs(UnmanagedType.LPStr)]String key);

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)
{
String retString;

retString = func_EncryptKey("This is it", "");
MessageBox.Show(retString);

retString = func_DecryptKey(retString,"");
MessageBox.Show(retString);
}
}
[/code]


[code delphi]
function func_EncryptKey(Src: PAnsiChar; Key: PAnsiChar) : PAnsiChar;stdcall;
begin
ShowMessage(Src);
ShowMessage(Key);
Result := PAnsiChar('ReturnString');
end;

function func_DecryptKey(Src: PAnsiChar; Key: PAnsiChar): PAnsiChar;stdcall;
begin
ShowMessage(Src);
ShowMessage(Key);
Result := PAnsiChar('ReturnString');
end;

exports func_EncryptKey;
exports func_DecryptKey;
[/code]

除錯第一要務,單純化,再求問題點
先確定 C# call dll 可以正常傳進傳出 正確的字串,再研究是哪部份的問題
編輯記錄
superrakce 重新編輯於 2011-07-14 03:49:46, 註解 無‧
superrakce 重新編輯於 2011-07-14 04:02:30, 註解 無‧
rick060
高階會員


發表:2
回覆:112
積分:217
註冊:2009-11-17

發送簡訊給我
#5 引用回覆 回覆 發表時間:2011-07-15 08:11:47 IP:60.250.xxx.xxx 未訂閱
function 後有 stdcall; 嗎

或在 DllImport 指定 Call Convention 配合 delphi 裡的宣告

[DllImport("Project1.dll",CallingConvention = CallingConvention.StdCall]
編輯記錄
rick060 重新編輯於 2011-07-14 18:45:57, 註解 無‧
superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#6 引用回覆 回覆 發表時間:2011-07-15 09:09:01 IP:114.33.xxx.xxx 訂閱
CallingConvention = CallingConvention.StdCall
最新測試結果,字串長度為12以上,執行第一次會成功,
第二次之後全都失敗(測試的都是同樣的字串),
都是同樣的錯誤

(在delphi 的dll 程式裡,在回傳前都會showmessage 要回傳的值,showmessage 都是正常的,只要回傳回來就出錯)

九點十五分測試結果:
修改 dll 檔:
//showmessage(s);
//result := PAnsiChar(s);
showmessage(s ' ' inttostr(length(s)));
result := PAnsiChar(s inttostr(length(s)));
這樣就不會出錯…真是太神奇了,不過這不是我要的功能,依舊看不出來問題在那?
===================引 用 rick060 文 章===================
function 後有 stdcall; 嗎

或在 DllImport 指定 Call Convention 配合 delphi 裡的宣告

[DllImport("Project1.dll",CallingConvention = CallingConvention.StdCall]
編輯記錄
superrakce 重新編輯於 2011-07-14 19:12:38, 註解 無‧
superrakce 重新編輯於 2011-07-14 19:13:19, 註解 無‧
superrakce 重新編輯於 2011-07-14 19:20:04, 註解 無‧
rick060
高階會員


發表:2
回覆:112
積分:217
註冊:2009-11-17

發送簡訊給我
#7 引用回覆 回覆 發表時間:2011-07-15 09:43:23 IP:60.250.xxx.xxx 未訂閱
我想你也不方更 po出你 dll 的 source
但可以給你一個方向

1.dll "BEGIN" 前 的 ebp/esp 與 "END"完 ret 前 的 ebp/esp 要一樣。 (CPU View)
2.function 內檢查是否有應釋放而未釋放的記憶體
3.function 內是否有 stack overflow 的可能
superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#8 引用回覆 回覆 發表時間:2011-07-15 10:12:27 IP:114.33.xxx.xxx 訂閱
1.ebp/esp?指的是堆疊嗎?DLL 裡面只用到一些字串處理函數,應該不會造成問題才是
2.裡面只有宣告INTGER 、 STRING 跟陣列 array[1..n] of TStrings; 而陣列在回傳前有把他 Free
3.不至於是 stack overflow ,因為回傳值加上一個字就不會出問題了。

最後測試結果:
加密的字串尾巴加上隨便一個字,回傳正常;
解密碼字串刪除最後一個字,解密成功,回傳正常。
附上「部份」程式碼;
這樣子雖然功能完成了,但我很想知道發生了什麼事,
在DELPHI 內 呼叫都沒這個問題,為何到了C#去呼叫就有這個毛病,怪哉

[code delphi]
function Encrypt(st: PAnsiChar):PAnsiChar;
var
a: array[1..n] of TStrings;
i,j,k,d,ra: integer;
t,ErrMsg,s:string;

begin
s:= st;
ErrMsg := Check(s);
if ErrMsg='' then begin
k := Length(s);
for i:=1 to n do a[i] := TStringList.Create;
ra := Ord(t[1])*1000 Ord(t[2])*100 Ord(t[3]);
for i:=1 to k do if Ord(s[i]) in [33..126] then t := t Chr(StrToInt(a[i][Ord(s[i]) - 33])) else t := t s[i];
for i:=1 to n do a[i].Free;
result := PAnsiChar(t ':.:');
end
else
result := PAnsiChar(ErrMsg);

end;

function Decrypt(ts:PAnsiChar):PAnsiChar;
var a: array[1..n] of TStrings;
ra,i,k: integer;
s,ErrMsg,t:string;
function FindNo(b,c: byte): char;
begin
FindNo := Chr(a[b].IndexOf(IntToStr(c)) 33);
end;

begin
t:= ts;
t:= leftstr(t,length(t)-3);
s := '';
ErrMsg := Check(t);
if ErrMsg='' then begin
k := Length(t);
for i:=1 to n do a[i] := TStringList.Create;
ra := Ord(t[1])*1000 Ord(t[2])*100 Ord(t[3]); Delete(t,1,3); k := k - 3;
for i:=1 to k do uniNO(ra i, a[i]);
for i:=1 to k do if Ord(t[i]) in [33..126] then s := s FindNo(i, Ord(t[i])) else s := s t[i];
for i:=1 to n do a[i].Free;
result := PAnsiChar(s ' ');

end
else
result := PAnsiChar(ErrMsg);
end;

[/code]

===================引 用 rick060 文 章===================
我想你也不方更 po出你 dll 的 source
但可以給你一個方向

1.dll "BEGIN" 前 的 ebp/esp 與 "END"完 ret 前 的 ebp/esp要一樣。 (CPU View)
2.function 內檢查是否有應釋放而未釋放的記憶體
3.function 內是否有 stack overflow 的可能
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#9 引用回覆 回覆 發表時間:2011-07-16 21:37:34 IP:122.126.xxx.xxx 訂閱
以組合語言的觀點來看,你那樣寫dll的方式感覺不太正確,僅管我對delphi的語法已不熟。

以加密來說,裡面的t 字串,是局部變數,當你把它的指標傳出去後,理論上t的內容已不是正確的那個,它變垃圾了,當然運氣好一點可能只有小破壞到,於是你以為是正確的。

你細想一些win32 api,是不是很少傳 char* 出來,都是要你把你想要的東西放到參數中,這時候這個參數是你事先配好的記憶區塊,所以不會出事,當然會配套要你傳入長度,以免放過頭…

請試一下把 加密裡的 t 移至 function 外面,讓它變成全域的。解密的 s 也變成全域的。 這時候t 與 s 不會因為function結束而被釋放。理論上才會是正常的。
然後再試一下,若從此沒問題,那就很肯定是我上面講的問題!

ps 全域後的名稱要換一下,不好又是s與t的,以免"打架"

以上是我另一種觀點 : )


===================引 用 superrakce 文 章===================

最後測試結果:
加密的字串尾巴加上隨便一個字,回傳正常;
解密碼字串刪除最後一個字,解密成功,回傳正常。
附上「部份」程式碼;
這樣子雖然功能完成了,但我很想知道發生了什麼事,
在DELPHI 內 呼叫都沒這個問題,為何到了C#去呼叫就有這個毛病,怪哉
------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2011-07-16 07:39:50, 註解 無‧
aftcast 重新編輯於 2011-07-16 07:52:56, 註解 無‧
aftcast 重新編輯於 2011-07-17 21:00:10, 註解 無‧
superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#10 引用回覆 回覆 發表時間:2011-07-18 09:20:44 IP:114.33.xxx.xxx 訂閱
aftcast 的方案解決問題

感謝您精彩的回覆,讓我學了不少

superrakce
一般會員


發表:24
回覆:35
積分:11
註冊:2006-10-09

發送簡訊給我
#11 引用回覆 回覆 發表時間:2011-07-20 14:43:23 IP:114.33.xxx.xxx 訂閱
大大,很抱歉在結案後測試發現新問題
在加密後存到資料庫讀出後會無法解密。
delphi 內測試過程 :
1. 加密Edit1 字串> 傳至edit2>解密edit2字串>還原正常
2.加密Edit1 字串> 寫入資料庫>讀出解密字串>還原異常(有部份字體亂碼)

c# 的測試則完全呈現 delphi 測試的第2種結果

小弟猜測是字元編碼的問題?

===================引 用 aftcast 文 章===================
以組合語言的觀點來看,你那樣寫dll的方式感覺不太正確,僅管我對delphi的語法已不熟。

以加密來說,裡面的t 字串,是局部變數,當你把它的指標傳出去後,理論上t的內容已不是正確的那個,它變垃圾了,當然運氣好一點可能只有小破壞到,於是你以為是正確的。

你細想一些win32 api,是不是很少傳 char* 出來,都是要你把你想要的東西放到參數中,這時候這個參數是你事先配好的記憶區塊,所以不會出事,當然會配套要你傳入長度,以免放過頭…

請試一下把 加密裡的 t 移至 function 外面,讓它變成全域的。解密的 s 也變成全域的。 這時候t 與 s 不會因為function結束而被釋放。理論上才會是正常的。
然後再試一下,若從此沒問題,那就很肯定是我上面講的問題!

ps 全域後的名稱要換一下,不好又是s與t的,以免"打架"

以上是我另一種觀點 : )


===================引 用 superrakce 文 章===================

最後測試結果:
加密的字串尾巴加上隨便一個字,回傳正常;
解密碼字串刪除最後一個字,解密成功,回傳正常。
附上「部份」程式碼;
這樣子雖然功能完成了,但我很想知道發生了什麼事,
在DELPHI 內 呼叫都沒這個問題,為何到了C#去呼叫就有這個毛病,怪哉
系統時間:2024-04-19 7:48:55
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!