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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

运行返回签名不正确_如果调用约定不匹配,会发生什么?

發布時間:2025/3/15 编程问答 91 豆豆
生活随笔 收集整理的這篇文章主要介紹了 运行返回签名不正确_如果调用约定不匹配,会发生什么? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

蝎子

信不信由你,調用約定不匹配是程序經常出問題的原因之一。當你的程序代碼中出現不相匹配的調動約定的時候,編譯器會”大吼大叫”,但是懶惰的程序員只會在其中進行強制轉換,以使編譯器”盡快閉嘴”。
結果是:Windows不得不永遠支持你編寫的錯誤代碼。

Windows窗口過程

有很多人會錯誤地聲明了Windows窗口過程(通常是將它聲明為__cdecl,而不是__stdcall),因為這個原因,我們分發消息給窗口過程的函數添加了額外的保護,這樣它就可以檢測到錯誤的函數聲明,并執行適當的修復。
這是你為什么在堆棧上經常會看到神秘的0xdcbaabcd的原因。向窗口過程分發消息的函數會檢查這個值是否在堆棧中的正確位置。如果不是,那么它會檢查窗口過程是否從堆棧中彈出了一個多余的雙字(如果是這樣的話,它將嘗試修復堆棧),或者窗口過程是否錯誤地聲明為__cdecl而不是__stdcall(如果是這樣的話,它將參數從窗口過程的堆棧中彈出)。

DirectX回調函數

在DirectX庫中大量地使用了回調函數,并且人們再次將其回調函數聲明為__cdecl,而不是__stdcall。因此DirectX枚舉器必須對這些錯誤聲明的函數進行特殊的堆棧清理。(真難啊…)

IShellFolder::CreateViewObject
我記得,曾經這么一個程序,它錯誤地聲明了一個名為CreateViewWindow的函數,并且開發者設法以某種方式欺騙了編譯器以接受它。如下圖所示:

他們不僅錯誤地聲明了函數簽名,而且在函數的內部,即使函數沒有成功完成,它也返回了S_OK。調用此函數后,我不得不添加額外的代碼來清理堆棧,并確認它的返回值是不是正確的。

Rundll32.exe入口點函數

在Microsoft的知識庫文章中,介紹了rundll32.exe調用的函數所需要的函數簽名。但這并沒有阻止人們使用rundll32調用那些并非由rundll32設計的隨機函數,例如user32模塊里的LockWorkStation或ExitWindowsEx。
下面,就讓我們來看看,如果使用rundll32.exe調用ExitWindowsEx之類的函數時,會發生什么情況。
rundll32.exe會解析其命令行,并假定函數的編寫方式如下:
void CALLBACK ExitWindowsEx(HWND hwnd, HINSTANCE hinst,LPSTR pszCmdLine, int nCmdShow);

但是,實際的ExitWindowsEx的函數原型是這樣的:
BOOL WINAPI ExitWindowsEx(UINT uFlags, DWORD dwReserved);

接下來會發生什么呢?
當進入ExitWindowsEx函數時,堆棧看起來像這樣:

但是,函數希望看到堆棧是這樣的:

會發生什么?

rundll32.exe傳遞的hwnd窗口句柄,會被誤認為是uFlags,而hinst會誤認為為dwReserved。由于窗口句柄是隨機的,因此最終會將這個隨機標志傳遞給ExitWindowsEx。也許今天是EWX_LOGOFF,明天是EWX_FORCE,下次是EWX_POWEROFF。

現在假設該函數設法返回。(例如,函數執行失敗)。ExitWindowsEx函數清除堆棧中的兩個參數,而不知道它已傳遞給四個參數。生成的堆棧是這樣的:

現在堆棧的數據已經被破壞了,真正有趣的事情即將發生。
例如,假設上圖中的”.. rest of the stack ..”里保存的是一個返回地址。好了,原始代碼將執行”返回”指令以通過該返回地址返回,但是在堆棧被破壞的情況下,”返回”指令將返回命令行并嘗試像執行代碼一樣嘗試執行它。

Random custom functions

開發者可能會將一個函數導出為__cdecl,但將其視為__stdcall。這似乎可行,但是返回時,堆棧將被破壞(因為調用者期望使用__stdcall函數來清理堆棧,但是得到的卻是無法使用的__cdecl函數)。
好的,我們已經舉了這么多的例子了。我想你應該可以明白我想表達的要點了,下面是一些你肯定會問的問題。

為什么編譯器不能捕獲這些錯誤呢?

編譯器確實這樣做了。(不是上面的那個rundll32例子啊)
但是人們似乎已經習慣了只使用強制轉換來關閉編譯器的警告。

我們看看下面的函數原型:
LRESULT CALLBACK DlgProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam);

上面的函數原型是錯誤的,正確的版本請看下面:
INT_PTR CALLBACK DialogProc(HWND hwndDlg, UINT uMsg,WPARAM wParam, LPARAM lParam);

如果你這樣調用:
DialogBox(hInst, MAKEINTRESOURCE(IDD_CONTROLS_DLG), hWnd, DlgProc);

編譯器會馬上發出如下的編譯器錯誤信息:
error C2664: ‘DialogBoxParamA’ : cannot convert parameter 4 from ‘LRESULT (HWND,UINT,WPARAM,LPARAM)’ to ‘DLGPROC’.

然后,你想懶惰一下,使用下面的強制轉換來關閉錯誤信息:
DialogBox(hInst, MAKEINTRESOURCE(IDD_CONTROLS_DLG), hWnd, reinterpret_cast(DlgProc));

“噢,拜托,誰會這么愚蠢,會在沒有實際修復代碼錯誤的情況下,僅僅是加入強制類型轉換來以使編譯錯誤消失?”
看來,大部分人都會這樣做。

在網絡上,我還看到了很多開發者犯了這樣的錯誤,這里就不一一列舉了。

帶有這些錯誤的程序代碼是如何工作的?
當然,這些程序在某種程度上可以正常工作,要不然使用者肯定會注意到并要求開發者修復這個錯誤。
但是,程序如何在被破壞的堆棧中生存呢?(這個問題我后面會回答)

總結

強制類型轉換,不應該出現在任何一個完美代碼主義者的字典里。
但,沒它,還真不行。

最后

Raymond Chen的《The Old New Thing》是我非常喜歡的博客之一,里面有很多關于Windows的小知識,對于廣大Windows平臺開發者來說,確實十分有幫助。
本文來自:《What can go wrong when you mismatch the calling convention?》

總結

以上是生活随笔為你收集整理的运行返回签名不正确_如果调用约定不匹配,会发生什么?的全部內容,希望文章能夠幫你解決所遇到的問題。

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