วิธีทำให้หน้าต่างคอนโซลเปิดใน Visual C ++


191

ฉันเริ่มต้นใน Visual C ++ และฉันต้องการทราบวิธีการเก็บหน้าต่างคอนโซล

เช่นนี้จะเป็นแอปพลิเคชัน "สวัสดีโลก" โดยทั่วไป:

int _tmain(int argc, _TCHAR* argv[])
{
    cout << "Hello World";
    return 0;
}

เส้นที่ฉันขาดหายไปคืออะไร?


Amruth A. Pillai รหัสของคุณไม่แสดง "กดปุ่มใด ๆ เพื่อดำเนินการต่อ" ขอบคุณ

คุณสามารถพิมพ์ด้วยตัวคุณเองด้วย std :: cout call ที่เรียบง่าย
Raúl Roa

5
ข้อเสียของโซลูชันที่เสนอทั้งหมดคือไม่มีวิธีใดที่ทำงานกับการดีบักได้ (Ctrl + F5 ล้มเหลวที่นี่) และเมื่อแอปพลิเคชันหยุดทำงานโดยไม่คาดคิด สิ่งที่ฉันชอบที่จะเห็นคือหน้าต่างคอนโซลแบบ in-IDE เช่น Eclipse และ IDE อื่น ๆ มี พวกเขาเพียงแค่แสดงผลลัพธ์ไปที่ stdout / stderr หลังจากที่โปรแกรมสิ้นสุดลง
ดร. Sybren

@sybren คำตอบที่ยอมรับนั้นใช้ได้กับ CTRL + F5 และทำไมคุณต้องการโซลูชันที่ทำงานกับการดีบัก (F5) แน่นอนว่าจุดแก้จุดบกพร่องทั้งหมดคือ .. debug? ประโยชน์ของการมีคอนโซลที่หยุดชั่วคราวหลังจากการยุติโปรแกรมในเซสชันการดีบักคืออะไร
JBentley

2
@JBentley Eclipse และ IDE อื่น ๆ ช่วยให้คุณอ่านผลลัพธ์ของโปรแกรมของคุณได้แม้หลังจากที่กระบวนการถูกยกเลิก แน่นอนคุณเห็นประโยชน์เพิ่มโดยเฉพาะอย่างยิ่งเมื่อพยายามหาจุดบกพร่อง? นอกจากนี้จุดพักจะทำงานเมื่อคุณทราบว่าโปรแกรมหยุดทำงานซึ่งอาจบอกได้ยากเมื่อเอาต์พุตหายไปจากหน้าจอของคุณ
ดร. Sybren

คำตอบ:


389

เริ่มต้นโครงการที่มีแทนเพียงCtrl+F5F5

หน้าต่างคอนโซลจะยังคงเปิดอยู่พร้อมกับPress any key to continue . . .ข้อความหลังจากที่โปรแกรมออก

โปรดทราบว่านี่ต้องใช้Console (/SUBSYSTEM:CONSOLE)ตัวเลือก linker ซึ่งคุณสามารถเปิดใช้งานได้ดังนี้:

  1. เปิดโครงการของคุณแล้วไปที่ Solution Explorer หากคุณกำลังติดตามกับฉันใน K&R "โซลูชัน" ของคุณจะเป็น 'สวัสดี' โดยมี 1 โครงการภายใต้โครงการนี้และ 'สวัสดี' ด้วยตัวหนา
  2. คลิกขวาที่ 'hello "(หรือชื่อโครงการของคุณ)
  3. เลือก "Properties" จากเมนูบริบท
  4. เลือกคุณสมบัติการกำหนดค่า> ตัวเชื่อมโยง> ระบบ
  5. สำหรับคุณสมบัติ "ระบบย่อย" ในบานหน้าต่างด้านขวาให้คลิกกล่องแบบเลื่อนลงในคอลัมน์ด้านขวา
  6. เลือก "คอนโซล (/ ระบบย่อย: คอนโซล)"
  7. คลิกนำไปใช้รอให้การดำเนินการเสร็จสิ้นตามที่ต้องการจากนั้นคลิกตกลง (หาก "Apply" เป็นสีเทาให้เลือกตัวเลือกระบบย่อยอื่น ๆ คลิก Apply จากนั้นกลับไปและใช้ตัวเลือก console ประสบการณ์ของฉันคือ OK โดยตัวมันเองจะไม่ทำงาน)

CTRL-F5 และคำใบ้ของระบบย่อยทำงานร่วมกัน; พวกเขาไม่ใช่ตัวเลือกที่แยกจากกัน

(ความอนุเคราะห์จาก DJMorreTX จากhttp://social.msdn.microsoft.com/Forums/en-US/vcprerelease/thread/21073093-516c-49d2-81c7-d960f6dc2ac6 )


36
สิ่งนี้จะรันโปรแกรมโดยไม่มีการดีบั๊ก มันจะดีกว่าถ้ามีวิธีแก้ปัญหาที่ทำงานได้ทั้งในโหมดดีบั๊กและโหมดรันปกติ
スーパーファミコン

3
สำหรับผู้ที่ไม่สามารถหาวิธีแก้ปัญหานี้ให้ทำงานในโครงการ makefile ได้เกิดจากบั๊กใน Visual Studio ฉันเพิ่งโพสต์คำตอบพร้อมการแก้ไข
JBentley

1
สามารถยืนยันได้! มีแอปคอนโซลที่ไม่ได้ทำงานและทำงานได้ คุณไม่จำเป็นต้องเปลี่ยนรหัสด้วยcin.get(),getchar(), system("pause")หรือขยะอื่น ๆ การเปลี่ยนงานนี้
Callat

Ctrl + F5 หมายถึง 'เริ่มต้นโดยไม่มีการดีบัก' ดังนั้นคุณไม่สามารถใช้สิ่งนี้เมื่อทำการดีบัก เพียงเพิ่มsystem("pause");ในตอนท้ายของรหัสของคุณ มันสมเหตุสมผลและทำงานได้ดี
Milad

41

วิธีมาตรฐานคือcin.get()ก่อนใบแจ้งยอดคืนสินค้าของคุณ

int _tmain(int argc, _TCHAR* argv[])
{
    cout << "Hello World";
    cin.get();
    return 0;
}

4
ฉันรู้ว่าเนื่องจากโปรแกรมใช้ cout ที่ได้รับการดูแลอยู่แล้ว แต่ฉันคิดว่ามันคุ้มค่าที่จะพูดถึงว่าคุณจะต้อง #include <iostream> และใช้ std namespace: std :: cin.get () .
Brian

7
ใช้งานได้ แต่ Ctrl + F5 ดีกว่าโดยเฉพาะอย่างยิ่งเมื่อติดตามการทำลายวัตถุทั่วโลกและอื่น ๆ
Ternary

8
_tmain-1 ฉันจะลงคะแนนอีก -1 สำหรับcin.get()แทนการวางเบรกพอยต์สำหรับ F5 หรือใช้ Ctrl F5 แต่ฉันอนุญาตให้ลงหนึ่งคะแนนเท่านั้น
ไชโยและ hth - Alf

8
@Cheers: มีอะไรผิดปกติกับ_tmain? นี่เป็นวิธีมาตรฐานในการเขียนแอปพลิเคชัน Windows ที่กำหนดเป้าหมายไปที่ระบบย่อยคอนโซล การเบี่ยงเบนจากมาตรฐานนั้นคือสิ่งที่จะเป็นการปฏิบัติที่ไม่ดี เห็นได้ชัดว่าไม่มีใครพูดถึงรหัสพกพาที่นี่ คำถามที่ว่า Visual C ++ และ_tmainเป็นลายเซ็นที่ปรากฏในตัวอย่างรหัส ได้เวลาละทิ้งศาสนานี้แล้ว Windows เป็น "มาตรฐาน" โดยค่าเริ่มต้นและมีเหตุผลที่ดีมากสำหรับการปฏิบัติตามมาตรฐาน
Cody Gray

8
@CodyGray: downvote ของฉัน_tmainคือเพราะมันไม่จำเป็นทั้งหมดที่ไม่ได้มาตรฐาน ( มาตรฐานระหว่างประเทศ C ++ ต้องมีธรรมดาmain) และเพราะมันใช้Tรูปแบบแมโครของ Microsoft ซึ่งเป็นภาวะแทรกซ้อนที่ไม่จำเป็นและ verbiage สำหรับการสนับสนุน Windows 9x tmainถ้าคุณรู้สึกว่าเบี่ยงเบนไปจากมาตรฐานคือการปฏิบัติที่ไม่ดีแล้วคุณควรอย่างยิ่งที่ได้ใช้ ไม่มีเหตุผลที่ดีสำหรับการใช้tmainยกเว้นสำหรับการหมุนรอบหรือสำหรับมืออาชีพสำหรับการแสดงความสามารถทั้งหมดของคน
ไชโยและ hth - Alf

24

วางเบรกพอยต์บนreturnบรรทัด

คุณกำลังเรียกใช้มันในตัวดีบั๊กใช่ไหม?


15
เวลาส่วนใหญ่เป็นเรื่องยากเนื่องจากอาจมีจุดทางออกหลายจุดในโปรแกรม
volzotan

โปรแกรมอาจไม่เริ่มทำงานเนื่องจาก DLL หายไปและไม่เคยถึงจุดหยุดพัก
Michael

18

ตัวเลือกอื่นคือการใช้

#include <process.h>
system("pause");

แม้ว่านี่จะไม่ได้พกพามากเพราะมันจะทำงานเฉพาะบน Windows แต่มันจะพิมพ์โดยอัตโนมัติ

กดปุ่มใดก็ได้เพื่อดำเนินการต่อ ...


1
ระบบ ("หยุดชั่วคราว") จะทำให้โปรเซสเซอร์ของคุณทำงานต่อไปซึ่งไม่ควรใช้ ใช้ cin.get () หรือเทียบเท่า
Krythic

system<stdlib.h>จะมีการประกาศใน
melpomene

@ Krythic ฉันเพิ่งลองและไม่ได้กิน CPU 100% การใช้โปรเซสเซอร์ของฉันอยู่ที่ 0% - 1% ตลอดเวลา ไม่สามารถทำซ้ำได้
melpomene

ระบบ ("หยุด") จะเรียกคำสั่ง "หยุด" ใน cmd ซึ่งไม่ได้ใช้ CPU ตลอดเวลา มันเทียบเท่ากับ _getche () เป็นหลัก
Paul Stelian

7

สำหรับโครงการ makefile โซลูชันที่ยอมรับล้มเหลวเนื่องจากข้อบกพร่องใน Visual Studio (ซึ่งมีอยู่อย่างน้อยจนถึงรุ่น 2012 - ฉันยังไม่ได้ทดสอบ 2013) ข้อผิดพลาดนี้เป็นรายละเอียดที่นี่

เพื่อให้หยุดชั่วคราวคอนโซลหลังจากโปรแกรมสิ้นสุดในโครงการ makefile ทำตามขั้นตอนเหล่านี้ (ซึ่งอาจแตกต่างกันไปสำหรับรุ่นอื่นที่ไม่ใช่ 2010 - 2012):

1) ส่งผ่าน/SUBSYSTEM:CONSOLEไปยังลิงเกอร์ - แก้ไข : ดูด้านล่าง

2) เปิดไฟล์โครงการของคุณ (.vcxproj) ในโปรแกรมแก้ไขข้อความ

3) ด้านใน<project>แท็กรูทใส่ต่อไปนี้

<ItemDefinitionGroup>
  <Link>
    <SubSystem>Console</SubSystem>
  </Link>
</ItemDefinitionGroup>

4) โหลดโครงการในโซลูชันของคุณ

5) เรียกใช้โปรแกรมโดยไม่มีการดีบัก (CTRL + F5)

แก้ไข:

ตามความคิดเห็นของฉันด้านล่างการตั้งค่าตัวเลือกลิงเกอร์/SUBSYSTEM:CONSOLEนั้นไม่เกี่ยวข้องกับโครงการ Makefile (และไม่จำเป็นต้องเป็นไปได้แม้แต่ในกรณีที่คุณใช้คอมไพเลอร์อื่นที่ไม่ใช่ MSVC) สิ่งที่สำคัญคือการตั้งค่าถูกเพิ่มลงในไฟล์. vcxproj ตามขั้นตอนที่ 3 ข้างต้น


ฉันไม่เคยได้ยินเกี่ยวกับขั้นตอนที่ 3 มาก่อน คุณแน่ใจหรือไม่ว่าจำเป็นและใช้งานได้?
Mooing Duck

@mooingduck ใช่และหลังจากความเห็นของฉันในคำตอบของคุณที่นี่ฉันได้ค้นพบแล้วว่าการส่งผ่าน/SUBSYSTEM:CONSOLEไปยังตัวเชื่อมโยงนั้นไม่เกี่ยวข้องจริง ๆ - ขั้นตอนที่ 3 เป็นสิ่งที่สำคัญ โปรดจำไว้ว่าคำตอบของฉันเกี่ยวข้องกับโครงการ makefile - ในโครงการ makefile IDE ไม่มีทางรู้ว่าคุณกำลังส่งอะไรไปยังตัวเชื่อมโยง (คุณอาจไม่ได้ใช้คอมไพเลอร์ที่มี/SUBSYSTEM:CONSOLEตัวเลือก) และเป็นโครงการ ตัวเองซึ่งติดตามว่ามันหมายถึงการเป็นโปรแกรมคอนโซล ฉันจะแก้ไขคำตอบของฉันตามนั้น
JBentley

1
@mooingduck ฉันสามารถยืนยันได้ว่าฉันใช้โซลูชันนี้ด้วยตัวเองในโครงการ makefile โดยที่ SCons เป็นระบบสร้างและ MSVC และ MinGW เป็นคอมไพเลอร์ ไม่มีวิธีอื่นที่ฉันรู้ว่าจะทำให้ IDE หยุดชั่วขณะคอนโซลหลังจากสิ้นสุดในโหมดที่ไม่ใช่การดีบัก
JBentley

1
@chuckleplant ฉันไม่กลัวเวิร์กโฟลว์ของฉันเป็นอีกทางหนึ่งเรียก SCons จาก VS ตอนแรกฉันสร้างโครงการ VS makefile ของฉันด้วยตนเองเพื่อให้พวกเขาส่งผ่านตัวแปรการกำหนดค่าไปยังสคริปต์ SCons ของฉัน (เช่น 32/64 บิตชื่อคอมไพเลอร์รุ่น / debug) ซึ่งจัดการกับตรรกะที่เหลือ ในการตั้งค่านั้นไม่จำเป็นต้องให้ไฟล์โครงการเปลี่ยนแปลงดังนั้นฉันจึงไม่ใช้คุณสมบัติสร้างอัตโนมัติของ SCons ฉันได้เปลี่ยนมาใช้ Linux แล้วดังนั้นฉันจะไม่ใช้ VS อีกต่อไป เนื่องจากเป็นข้อบกพร่องของ VS จึงอาจคุ้มค่าที่จะส่งคำขอคุณลักษณะไปที่ SCons เพื่อจัดการกับขั้นตอนพิเศษที่จำเป็น
JBentley

1
หรือคุณอาจรวมรหัส Python บางส่วนไว้ในสคริปต์ SCons ของคุณเพื่อทำด้วยตัวเองทุกครั้งที่มีการสร้างไฟล์โครงการ ฉันเชื่อว่าไฟล์โครงการ VS เป็นไปตามมาตรฐาน XML ดังนั้นจึงควรเพิ่มองค์ประกอบที่ขาดหายไปค่อนข้างง่ายและควรใช้โค้ดเพียงไม่กี่บรรทัด ฉันขอแนะนำให้เริ่มต้นที่นี่ (สำหรับ Python 2.x) หรือที่นี่ (3.x) คำตอบนี้อาจเป็นที่สนใจ
JBentley

4

คุณสามารถใช้cin.get();หรือcin.ignore();ก่อนคำสั่งการส่งคืนเพื่อหลีกเลี่ยงการปิดหน้าต่างคอนโซล


4

เพียงใส่เบรกพอยต์บนวงเล็บปีกกาสุดท้ายของ main

    int main () {
       //...your code...
       return 0;
    } //<- breakpoint here

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


3

เพียงเพิ่มเบรกพอยต์เข้ากับวงเล็บปิดของ_tmainวิธีการของคุณ นี่เป็นวิธีที่ง่ายกว่าโดยที่คุณไม่ต้องเพิ่มรหัสเพื่อทำการดีบั๊ก


2

main()วางเบรกพอยต์ในรั้งสิ้นสุดของ มันจะได้รับการสะดุดแม้จะมีหลายreturnงบ ข้อเสียเพียงอย่างเดียวคือการโทรไปexit()ไม่ถูกจับ

หากคุณไม่ได้แก้จุดบกพร่องให้ทำตามคำแนะนำในคำตอบ Zoidberg และเริ่มต้นโปรแกรมของคุณด้วยCtrl+ แทนเพียงF5F5


2

2 เซนต์ของฉัน:

ตัวเลือก 1: เพิ่มเบรกพอยต์เมื่อสิ้นสุด main()

ตัวเลือก 2: เพิ่มรหัสนี้ก่อนreturn 0;:

std::cout << "Press ENTER to continue..."; //So the User knows what to do
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

คุณต้องรวม<iomanip>ไว้สำหรับstd::numeric_limits


2

เพียงเพิ่มระบบ ("หยุด") ที่ส่วนท้ายของรหัสก่อนส่งคืน 0 เช่นนี้

#include <stdlib.h>

int main()
{
    //some code goes here
    system("pause")
    return 0;
}



1

ฉันรวม#include <conio.h>แล้วเพิ่มgetch();ก่อนreturn 0;บรรทัด นั่นคือสิ่งที่ฉันเรียนที่โรงเรียน วิธีการดังกล่าวข้างต้นที่นี่ค่อนข้างแตกต่างที่ฉันเห็น


2
-1: นอกเหนือจากความจริงที่ว่าการหยุดโปรแกรมเองนั้นมักจะเป็นวิธีการแก้ปัญหาที่ผิดพลาด (เนื่องจากในกรณีส่วนใหญ่ที่ไม่ใช่พฤติกรรมที่คุณต้องการให้ไบนารีที่เผยแพร่ของคุณมี) conio.h นั้นไม่ใช่มาตรฐานล้าสมัยและเป็น ส่วนหัว C ไม่ใช่ C ++! น่าเสียดายที่มีการฝึกเขียนโปรแกรมที่ไม่ดีมากมายในโรงเรียน
JBentley

หนึ่งคาดว่าจะใช้สิ่งนี้ระหว่างการทดสอบเท่านั้นไม่ใช่สิ่งที่คุณต้องการในเวอร์ชันสุดท้าย หากคุณกำลังเข้ารหัสสำหรับ Windows ปัญหาคืออะไรกับ #include <conio.h> / _getch () ;? การเขียนอย่างรวดเร็วกว่า cin.get () ไม่จำเป็นต้องกด 2 คีย์ (อย่างน้อยอักขระ + enter) และไม่สามารถใช้งานได้เฉพาะใน debug หรือเฉพาะในโหมด release แล้วมีอะไรผิดปกติ?
Barnack

1

มีปัญหาเดียวกัน ฉันใช้ _getch()ก่อนแถลงการคืนสินค้า มันได้ผล.


คุณอาจต้องการเพิ่มตัวอย่างและคำอธิบายในรหัสของคุณโดยเฉพาะอย่างยิ่งสำหรับคำถามที่ถาม / ตอบเมื่อนานมาแล้ว นอกจากนี้คุณรับสายจะทำหน้าที่เหมือนกับคำตอบเก่า ๆ หลายข้อและใช้การโทรเดียวกันกับคำตอบอื่น
Clockwork-Muse

0

(ตัวเลือกบางตัวอาจมีชื่อเรียกต่างกันฉันไม่ได้ใช้เวอร์ชั่นภาษาอังกฤษ)

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


0

ผมมีปัญหาเหมือนกัน; ในใบสมัครของฉันมีจุดทางออกหลายจุด () และไม่มีวิธีที่จะรู้ว่ามันอยู่ที่ไหนแล้วฉันก็รู้เรื่องนี้:

atexit(system("pause"));

หรือ

atexit(cin.get());

วิธีนี้จะหยุดไม่ว่าเราจะออกจากที่ใดในโปรแกรม


atexitไม่มีผู้ที่มีการโทรที่ถูกต้อง atexitใช้ตัวชี้ฟังก์ชั่นไม่ใช่จำนวนเต็ม
melpomene

0

ตัวเลือกอื่น:

#ifdef _WIN32
#define MAINRET system("pause");return 0
#else
#define MAINRET return 0
#endif

ในหลัก:

int main(int argc, char* argv[]) {
    MAINRET;
}

0

ที่จริงแล้วทางออกที่แท้จริงคือการเลือกแม่แบบโครงการเอง คุณต้องเลือก Win32 Console Application ใน VS ที่เก่ากว่าหรือกรอกชื่อโครงการก่อนแล้วคลิกสองครั้งที่ตัวช่วยสร้าง Windows Desktop แล้วเลือก Win32 console application จากนั้นเลือกโครงการที่ว่างเปล่า ณ จุดนี้ สิ่งนี้จะช่วยให้สิ่งที่ผู้ถามเดิมต้องการจริงๆโดยไม่ต้องเพิ่มจุดหยุดพิเศษและถือรหัส ฉันเจอปัญหานี้เช่นกัน คำตอบคือที่เว็บไซต์ MSDN


0

นี่คือวิธีที่จะทำให้หน้าต่างคำสั่งเปิดอยู่โดยไม่คำนึงถึงว่าจะหยุดการทำงานอย่างไรโดยไม่ต้องแก้ไขโค้ดใด ๆ :

ใน Visual Studio เปิดหน้าคุณสมบัติโครงการ -> การแก้จุดบกพร่อง

สำหรับCommandให้ป้อน$(ComSpec)

สำหรับข้อโต้แย้งคำสั่ง/k $(TargetPath)ให้ใส่ ต่อท้ายอาร์กิวเมนต์ใด ๆ กับแอปพลิเคชันของคุณเอง

ตอนนี้F5หรือCtrl-F5ดำเนินการWindows / System32 / cmd.exeในหน้าต่างใหม่และ/ kทำให้มั่นใจได้ว่าพรอมต์คำสั่งยังคงเปิดอยู่หลังจากการดำเนินการเสร็จสมบูรณ์

ข้อเสียคือการดำเนินการจะไม่หยุดในจุดพัก


0

อย่างที่บางคนชี้ไปแล้ววิธีแก้ปัญหาของ Zoidberg ไม่ได้แนบตัวดีบั๊กซึ่งเป็นสิ่งที่คุณไม่ต้องการ

ตัวเลือกที่ดีที่สุดคือการกำหนดค่า VS ของคุณตามลำดับ (ตั้งแต่ VS 2017 เป็นต้นไป) โดยไปที่เครื่องมือ> ตัวเลือก> การดีบัก> ทั่วไป คุณยกเลิกการเลือก "ปิดคอนโซลโดยอัตโนมัติเมื่อทำการดีบั๊กหยุด" (ที่ด้านล่างสุด) ซึ่งอาจถูกตรวจสอบในกรณีของคุณ


-1

คุณสามารถใส่ keep_window_open (); ก่อนที่จะกลับมาที่นี่เป็นตัวอย่างหนึ่ง

int main()
{
    cout<<"hello world!\n";
    keep_window_open ();
    return 0;
}

2
keep_window_open () มาจากไหนเช่นไฟล์ส่วนหัวและคลังใด
Tom Goodfellow

จากนี้ std_lib_facilities.h แต่มากที่สุดเท่าที่คุณมีมันรวม
Sifis Babionitakis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.