__stdcall คืออะไร


151

ฉันเรียนรู้เกี่ยวกับการเขียนโปรแกรม Win32 และWinMainต้นแบบดูเหมือนว่า:

int WINAPI WinMain ( HINSTANCE instance, HINSTANCE prev_instance, PSTR cmd_line, int cmd_show )

ฉันสับสนว่าWINAPIตัวระบุนี้ใช้สำหรับอะไรและพบว่า:

#define WINAPI      __stdcall

สิ่งนี้ทำอะไร ฉันสับสนโดยการมีบางอย่างหลังจากกลับมาประเภท มี__stdcallไว้เพื่ออะไร มันหมายความว่าอย่างไรเมื่อมีบางอย่างระหว่างชนิดที่ส่งคืนและชื่อฟังก์ชัน


2
@AndrewProck การเปลี่ยนแปลง "จำลอง" ไม่เป็นไรในกรณีนี้ แต่โดยทั่วไปคุณสามารถใช้ s เพื่อหลีกเลี่ยงความโง่ (และตอบโต้การผลิต - จุดในกรณี) ขั้นต่ำ 6 ตัวอักษร
Adi Inbar

คำตอบ:


170

__stdcallคือระเบียบการเรียกที่ใช้สำหรับฟังก์ชัน สิ่งนี้บอกคอมไพเลอร์กฎที่ใช้สำหรับการตั้งค่ากองซ้อนการผลักดันข้อโต้แย้งและรับค่าตอบแทน

มีจำนวนของการเรียกประชุมอื่น ๆ ที่มี__cdecl, __thiscall, และชื่อที่เยี่ยมยอด __fastcall เป็นแบบแผนการโทรมาตรฐานสำหรับการเรียกระบบ Win32__declspec(naked)__stdcall

วิกิพีเดียครอบคลุมรายละเอียด

ส่วนใหญ่จะสำคัญเมื่อคุณกำลังเรียกใช้ฟังก์ชันนอกรหัสของคุณ (เช่น OS API) หรือระบบปฏิบัติการกำลังเรียกคุณ (เช่นเดียวกับ WinMain) หากคอมไพเลอร์ไม่ทราบวิธีการเรียกที่ถูกต้องคุณอาจจะได้รับข้อผิดพลาดที่แปลกมากเนื่องจากสแตกจะไม่ได้รับการจัดการอย่างถูกต้อง


8
ดูคำถามนี้สำหรับตัวอย่างของ starnge ล่มมากstackoverflow.com/questions/696306/…
sharptooth

ถ้าฉันไม่เข้าใจผิดอนุสัญญาการเรียกเหล่านี้จะควบคุมวิธีที่คอมไพเลอร์สร้างรหัสแอสเซมบลี เมื่อเชื่อมต่อกับรหัสแอสเซมบลีจึงเป็นเรื่องสำคัญที่จะต้องทราบถึงการเรียกประชุมเพื่อป้องกันปัญหาสแต็ก มีตารางที่ดีที่นี่จัดทำเอกสารการประชุมบางอย่าง: msdn.microsoft.com/en-us/library/984x0h58.aspx
Nicholas Miller

เราสามารถพูดได้ไหมว่าสิ่งนี้จะปิดการใช้งานการเพิ่มประสิทธิภาพที่ผิดกฎหมายบางอย่าง?
kesari

40

C หรือ C ++ เองไม่ได้กำหนดตัวระบุเหล่านั้น พวกเขาเป็นส่วนขยายของคอมไพเลอร์และยืนสำหรับการเรียกประชุมบางอย่าง ที่กำหนดว่าจะวางข้อโต้แย้งตามลำดับที่ฟังก์ชั่นที่เรียกว่าจะหาที่อยู่ผู้ส่งและอื่น ๆ ตัวอย่างเช่น __fastcall หมายความว่าข้อโต้แย้งของฟังก์ชั่นจะถูกส่งผ่านการลงทะเบียน

วิกิพีเดียบทความให้ภาพรวมของการประชุมที่แตกต่างกันพบโทรออกมี


18

คำตอบที่ได้ครอบคลุมถึงรายละเอียด แต่ถ้าคุณไม่ได้ตั้งใจที่จะลงไปที่การชุมนุมแล้วสิ่งที่คุณต้องรู้ก็คือทั้งผู้โทรและผู้รับจะต้องใช้การประชุมการโทรเดียวกันมิฉะนั้นคุณจะได้รับข้อบกพร่องที่ หายาก


12

ฉันยอมรับว่าคำตอบทั้งหมดถูกต้องแล้ว แต่นี่คือเหตุผล คอมไพเลอร์ C และ C ++ ของ Microsoft ให้การประชุมการเรียกที่หลากหลายสำหรับความเร็ว (ตั้งใจ) ของการเรียกฟังก์ชันภายในฟังก์ชัน C และ C ++ ของแอปพลิเคชัน ในแต่ละกรณีผู้โทรและผู้รับอนุญาตต้องตกลงกันว่าจะใช้การประชุมแบบใด ตอนนี้ Windows เองก็มีฟังก์ชั่น (API) และสิ่งเหล่านั้นได้ถูกคอมไพล์แล้วดังนั้นเมื่อคุณเรียกมันว่าคุณจะต้องปฏิบัติตาม การเรียกใช้ Windows API และการเรียกกลับจาก Windows API ต้องใช้ __stdcall แบบแผน


3
และจะต้องไม่สับสนกับ _standard_call เพราะมันเป็นแบบมาตรฐาน! อาจคิดว่าเป็นจุด __stdcall ถ้าไม่มีใครรู้ดีกว่า
โยฮันเนส Schaub - litb

3
nitpick ขนาดเล็ก: มี Windows API บางตัวที่ใช้ __cdecl แทนที่จะเป็น __stdcall - โดยทั่วไปจะเป็นคนที่รับพารามิเตอร์ตัวแปรจำนวนมากเช่น wsprintf ()
Michael Burr

คุณถูก. มันตั้งชื่อให้ดูเหมือนฟังก์ชั่น CRT แต่มันเป็น API คุณรู้วิธีการ P / เรียกใช้จาก C # โดยบังเอิญหรือไม่?
โปรแกรมเมอร์ Windows

ฉันยังไม่ได้ทดสอบสิ่งนี้ แต่ pinvoke.net ให้ลายเซ็นนี้: "static extern int wsprintf ([ออก] StringBuilder lpOut, สตริง lpFmt, ... );"
Michael Burr

สัญชาตญาณของฉันบอกว่าคอมไพเลอร์ C # ไม่รู้ว่าจะใช้ระเบียบ __cdecl กับอันนั้น
โปรแกรมเมอร์ Windows


5

มันเกี่ยวข้องกับการเรียกใช้ฟังก์ชันโดยพื้นฐานแล้วลำดับที่สิ่งต่าง ๆ ถูกวางลงบนสแต็กและใครเป็นผู้รับผิดชอบในการล้างข้อมูล

นี่คือเอกสาร แต่มันไม่ได้มีความหมายอะไรมากถ้าคุณไม่เข้าใจส่วนแรก:
http://msdn.microsoft.com/en-us/library/zxk0tw93.aspx


4

__stdcall ใช้เพื่อใส่อาร์กิวเมนต์ของฟังก์ชันในสแต็ก หลังจากเสร็จสิ้นการใช้งานฟังก์ชั่นนี้จะทำการยกเลิกการจัดสรรหน่วยความจำโดยอัตโนมัติ ใช้สำหรับอาร์กิวเมนต์ที่คงที่

void __stdcall fnname ( int, int* )
{
    ...
}

int main()
{
    CreateThread ( NULL, 0, fnname, int, int*...... )
}

นี่คือfnname args มันผลักเข้าไปในกองโดยตรง


1

ฉันไม่เคยใช้สิ่งนี้มาก่อนจนกระทั่งวันนี้ มันเป็นเพราะในรหัสของฉันฉันใช้มัลติเธรดและ API หลายเธรดที่ฉันใช้คือ windows one (_beginthreadex)

ในการเริ่มต้นเธรด:

_beginthreadex(NULL, 0, ExecuteCommand, currCommand, 0, 0);

ฟังก์ชัน ExecuteCommand ต้องใช้คีย์เวิร์ด __stdcall ในลายเซ็นเมธอดเพื่อให้ startthreadex เรียกมัน:

unsigned int __stdcall Scene::ExecuteCommand(void* command)
{
    return system(static_cast<char*>(command));
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.