ada digunakan untuk menjadi pencarian kesalahan exe di bagian alat di studio visual yang melakukan ini dengan cukup baik ketika Anda hanya perlu pesan dari kesalahan untuk debugging.
ColdCat
@ColdCat: Untuk debugging, jauh lebih mudah untuk hanya menambahkan @err,hrarloji, dan minta debugger secara otomatis mengonversi kode kesalahan terakhir ke representasi yang dapat dibaca manusia. Penentu ,hrformat bekerja untuk setiap ekspresi yang mengevaluasi ke nilai integral, misalnya 5,hrarloji akan menampilkan "ERROR_ACCESS_DENIED: Akses ditolak." .
IInspectable
2
Dari GetLastError()dokumentasi: " Untuk mendapatkan string kesalahan untuk kode kesalahan sistem, gunakan FormatMessage()fungsinya. ". Lihat contoh Mengambil Kode Kesalahan Terakhir di MSDN.
Remy Lebeau
Jawaban:
145
//Returns the last Win32 error, in string format. Returns an empty string if there is no error.
std::stringGetLastErrorAsString(){//Get the error message, if any.
DWORD errorMessageID =::GetLastError();if(errorMessageID ==0)return std::string();//No error message has been recorded
LPSTR messageBuffer =nullptr;size_t size =FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),(LPSTR)&messageBuffer,0, NULL);
std::string message(messageBuffer, size);//Free the buffer.LocalFree(messageBuffer);return message;}
Saya percaya Anda benar-benar harus lulus (LPSTR)&messageBufferdalam kasus ini, karena jika tidak, FormatMessageA tidak dapat mengubah nilainya untuk menunjuk ke buffer yang dialokasikan.
Kylotan
2
Oh, wow, ya itu agak aneh. Bagaimana cara memodifikasi pointer? Tapi menyampaikannya alamat penunjuk (pointer-to-a-pointer), tetapi melemparkannya ke pointer biasa ... Keanehan Win32. Terima kasih untuk kepala, perbaiki di basis kode saya sendiri (dan jawaban saya). Tangkapan yang sangat halus.
Jamin Grey
1
Terima kasih banyak, contoh Anda jauh lebih jelas daripada yang dari MSDN. Terlebih lagi, yang dari MSDN bahkan tidak bisa dikompilasi. Ini termasuk beberapa strsafe.hheader, yang tidak aman sama sekali, itu menyebabkan banyak kesalahan kompiler di winuser.hdan winbase.h.
Hi-Angel
2
Beberapa ID kesalahan tidak didukung. Misalnya, 0x2EE7, ERROR_INTERNET_NAME_NOT_RESOLVED menyebabkan kesalahan baru saat memanggil FormatMessage: 0x13D, Sistem tidak dapat menemukan teks pesan untuk nomor pesan 0x% 1 dalam file pesan untuk% 2.
Brent
3
Masalah dengan implementasi ini: 1GetLastErrorberpotensi dipanggil terlambat. 2Tidak ada dukungan Unicode. 3Penggunaan pengecualian tanpa menerapkan jaminan keamanan pengecualian.
IInspectable
65
Diperbarui (11/2017) untuk mempertimbangkan beberapa komentar.
@ Hi-Angel - Contoh ini mengasumsikan bahwa Anda mengkompilasi dengan UNICODE didefinisikan 'FormatMessage' sebenarnya adalah makro yang diperluas ke 'FormatMessageA' untuk buffer karakter Ansi / MBCS, atau 'FormatMessageW' untuk buffer UTF16 / UNICODE, tergantung pada bagaimana aplikasi dikompilasi. Saya mengambil kebebasan mengedit contoh di atas untuk secara eksplisit memanggil versi yang cocok dengan tipe buffer output (wchar_t).
Bukes
2
Tambahkan FORMAT_MESSAGE_IGNORE_INSERTS: "Jika Anda tidak bisa mengendalikan string format, maka Anda harus melewati FORMAT_MESSAGE_IGNORE_INSERTS untuk mencegah% 1 dari menyebabkan masalah." blogs.msdn.microsoft.com/oldnewthing/20071128-00/?p=24353
Roi Danton
1
Masalah dengan implementasi ini: 1Kegagalan untuk menentukan FORMAT_MESSAGE_IGNORE_INSERTSbendera penting . 2GetLastErrorberpotensi dipanggil terlambat. 3Pembatasan pesan yang sewenang-wenang menjadi 256 unit kode. 4Tidak ada penanganan kesalahan.
IInspectable
1
sizeof (buf) harus ARRAYSIZE (buf) karena FormatMessage mengharapkan ukuran buffer di TCHAR, bukan dalam byte.
Kai
1
Tidak bisakah kita menggunakan 256bukan (sizeof(buf) / sizeof(wchar_t)? apakah itu dapat diterima dan aman?
BattleTested
22
MSDN memiliki beberapa kode sampel yang menunjukkan cara menggunakan FormatMessage()dan GetLastError()bersama - sama: Mengambil Kode Kesalahan Terakhir
GetLastError mengembalikan kode kesalahan numerik. Untuk mendapatkan pesan kesalahan deskriptif (misalnya, untuk ditampilkan kepada pengguna), Anda dapat memanggil FormatMessage :
// This functions fills a caller-defined character buffer (pBuffer)// of max length (cchBufferLength) with the human-readable error message// for a Win32 error code (dwErrorCode).// // Returns TRUE if successful, or FALSE otherwise.// If successful, pBuffer is guaranteed to be NUL-terminated.// On failure, the contents of pBuffer are undefined.
BOOL GetErrorMessage(DWORD dwErrorCode, LPTSTR pBuffer, DWORD cchBufferLength){if(cchBufferLength ==0){return FALSE;}
DWORD cchMsg =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,/* (not used with FORMAT_MESSAGE_FROM_SYSTEM) */
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
pBuffer,
cchBufferLength,
NULL);return(cchMsg >0);}
Di C ++, Anda dapat menyederhanakan antarmuka dengan menggunakan kelas std :: string:
#include<Windows.h>#include<system_error>#include<memory>#include<string>typedef std::basic_string<TCHAR>String;StringGetErrorMessage(DWORD dwErrorCode){
LPTSTR psz{nullptr};const DWORD cchMsg =FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM
| FORMAT_MESSAGE_IGNORE_INSERTS
| FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,// (not used with FORMAT_MESSAGE_FROM_SYSTEM)
dwErrorCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),reinterpret_cast<LPTSTR>(&psz),0,
NULL);if(cchMsg >0){// Assign buffer to smart pointer with custom deleter so that memory gets released// in case String's c'tor throws an exception.auto deleter =[](void* p){::LocalFree(p);};
std::unique_ptr<TCHAR,decltype(deleter)> ptrBuffer(psz, deleter);returnString(ptrBuffer.get(), cchMsg);}else{auto error_code{::GetLastError()};throw std::system_error( error_code, std::system_category(),"Failed to retrieve error message string.");}}
CATATAN: Fungsi-fungsi ini juga berfungsi untuk nilai HRESULT. Cukup ubah parameter pertama dari DWORD dwErrorCode ke HRESULT hResult. Sisa kode dapat tetap tidak berubah.
Implementasi ini memberikan peningkatan berikut atas jawaban yang ada:
Kode sampel lengkap, bukan hanya referensi ke API untuk menelepon.
Menyediakan implementasi C dan C ++.
Bekerja untuk pengaturan proyek Unicode dan MBCS.
Mengambil kode kesalahan sebagai parameter input. Ini penting, karena kode kesalahan terakhir utas hanya valid pada titik yang ditentukan dengan baik. Parameter input memungkinkan pemanggil untuk mengikuti kontrak yang didokumentasikan.
Menerapkan keamanan pengecualian yang tepat. Tidak seperti semua solusi lain yang secara implisit menggunakan pengecualian, implementasi ini tidak akan membocorkan memori seandainya pengecualian dilemparkan saat membangun nilai kembali.
Penanganan / pelaporan kesalahan yang tepat, tidak seperti beberapa jawaban lainnya, yang mengabaikan kesalahan secara diam-diam.
Jawaban ini telah dimasukkan dari Dokumentasi Stack Overflow. Pengguna berikut telah berkontribusi pada contoh: stackptr , Ajay , Cody Grey ♦ , IInspectable .
Alih-alih melempar std::runtime_error, saya menyarankan untuk melempar di std::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")mana lastErrorakan menjadi nilai balik GetLastError()setelah FormatMessage()panggilan gagal .
zett42
Apa keuntungan menggunakan versi C Anda FormatMessagesecara langsung?
Micha Wiedenmann
@mic: Itu seperti bertanya: "Apa kelebihan fungsi dalam C?" Fungsi mengurangi kompleksitas dengan menyediakan abstraksi. Dalam hal ini, abstraksi mengurangi jumlah parameter dari 7 menjadi 3, mengimplementasikan kontrak terdokumentasi dari API dengan melewati flag dan parameter yang kompatibel, dan menyandikan logika pelaporan kesalahan yang terdokumentasi. Ini menyediakan antarmuka yang ringkas, dengan semantik yang mudah ditebak, sehingga Anda tidak perlu berinvestasi 11 menit untuk membaca dokumentasi .
IInspectable
Saya suka jawaban ini, sangat jelas bahwa perhatian yang cermat telah diberikan pada kebenaran kode. Saya punya dua pertanyaan. 1) Mengapa Anda menggunakan ::HeapFree(::GetProcessHeap(), 0, p)dalam deleter, bukan ::LocalFree(p)seperti yang ditunjukkan oleh dokumentasi? 2) Saya menyadari bahwa dokumentasi mengatakan untuk melakukannya dengan cara ini, tetapi tidak reinterpret_cast<LPTSTR>(&psz)melanggar aturan aliasing yang ketat?
Teh JoE
@teh: Terima kasih atas umpan baliknya. Pertanyaan 1): Jujur, saya tidak tahu apakah ini aman. Saya tidak ingat, siapa atau mengapa panggilan HeapFreeitu diperkenalkan, sementara ini masih menjadi topik dalam Dokumentasi SO. Saya harus menyelidikinya (walaupun dokumentasinya tampak jelas, bahwa ini tidak aman). Pertanyaan 2): Ini dua kali lipat. Untuk konfigurasi MBCS, LPTSTRadalah alias untuk char*. Para pemain selalu aman. Untuk konfigurasi Unicode, casting ke wchar_t*adalah mencurigakan pada tingkat apa pun. Saya tidak tahu apakah ini aman di C, tetapi kemungkinan besar tidak di C ++. Saya harus menyelidiki ini juga.
IInspectable
13
Secara umum, Anda perlu menggunakan FormatMessageuntuk mengkonversi dari kode kesalahan Win32 ke teks.
Memformat string pesan. Fungsi ini membutuhkan definisi pesan sebagai input. Definisi pesan dapat berasal dari buffer yang diteruskan ke fungsi. Itu bisa berasal dari sumber daya tabel pesan dalam modul yang sudah dimuat. Atau pemanggil dapat meminta fungsi untuk mencari sumber daya tabel pesan sistem untuk definisi pesan. Fungsi menemukan definisi pesan dalam sumber daya tabel pesan berdasarkan pengidentifikasi pesan dan pengidentifikasi bahasa. Fungsi menyalin teks pesan yang diformat ke buffer output, memproses urutan penyisipan yang disematkan jika diminta.
Saya mengkonfirmasi kode ini berfungsi. Bukankah seharusnya TS menerima jawaban ini?
swdev
2
Jika perlu untuk melempar lebih lanjut, ada cara yang lebih sederhana untuk melakukannya di C # dengan Win32Exception
SerG
2
@swdev: Mengapa orang harus menerima jawaban dalam C # untuk pertanyaan yang ditandai c atau c ++ ? Seperti berdiri, jawaban ini bahkan tidak menjawab pertanyaan yang diajukan.
IInspectable
1
Yah, saya tidak ingat pernah memberikan suara untuk jawaban yang diajukan ini. Saya membahas kelemahan yang jelas dalam logika @ swdev. Tetapi karena Anda tidak akan mempercayai saya, saya sekarang akan membuktikannya kepada Anda: Di sini, lakukan voting lagi. Yang itu dari saya, karena jawaban ini - walaupun mungkin berguna diberikan pertanyaan yang berbeda - sama sekali tidak berguna mengingat pertanyaan yang diajukan.
IInspectable
2
"Saya kira Anda memiliki wawasan yang membantu untuk menawarkan di luar yang jelas" - Memang, saya punya .
IInspectable
4
Jika Anda perlu mendukung MBCS dan juga Unicode, jawaban Mr.C64 tidak cukup. Buffer harus dinyatakan TCHAR, dan dilemparkan ke LPTSTR. Perhatikan bahwa kode ini tidak berurusan dengan baris baru yang menjengkelkan yang ditambahkan Microsoft ke pesan kesalahan.
Dalam hal CStringc'tor melempar pengecualian, implementasi ini bocor memori yang dialokasikan oleh panggilan ke FormatMessage.
IInspectable
Benar, tapi saya sudah menggunakan kode ini selama bertahun-tahun dan itu tidak pernah menjadi masalah. Satu-satunya kasus di mana CString ctor cenderung melempar adalah kegagalan untuk mengalokasikan memori, dan sebagian besar kode MFC - termasuk hal-hal yang disediakan Microsoft - tidak menangani kondisi memori seanggun yang Anda inginkan. Untungnya sebagian besar PC sekarang memiliki begitu banyak memori sehingga Anda harus bekerja sangat keras untuk menggunakannya semuanya. Setiap penggunaan yang menghasilkan instance CString sementara (termasuk mengembalikan CString) berisiko ini. Ini adalah risiko / kenyamanan trade-off. Juga jika itu terjadi dalam penangan pesan, pengecualian akan ditangkap.
korbanofleisure
Sebagian besar komentar ini salah, maaf. "Itu tidak pernah terjadi pada saya" adalah titik lemah untuk dibuat, terutama ketika Anda tahu bagaimana kode bisa gagal. Jumlah memori tidak memiliki dampak pada ruang alamat yang tersedia yang dialokasikan untuk suatu proses. RAM hanyalah optimasi kinerja. Copy-elision mencegah alokasi sementara, ketika kode ditulis untuk memungkinkan NRVO. Apakah pengecualian tertangkap dalam penangan pesan tergantung pada bitness proses dan pengaturan eksternal. Saya telah mengirimkan jawaban yang menunjukkan, bahwa manajemen risiko tidak mengarah pada ketidaknyamanan.
IInspectable
Selain itu, risiko kehabisan memori dalam membangun sementara adalah diperdebatkan. Pada saat itu semua sumber daya telah dibebaskan, dan tidak ada hal buruk yang akan terjadi.
IInspectable
1
Tidak, maaf kode ini tidak berguna untuk Anda. Tapi TBH cukup rendah dalam daftar masalah saya.
korbanofleisure
3
voidWinErrorCodeToString(DWORD ErrorCode,string&Message){char* locbuffer = NULL;
DWORD count =FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, NULL,ErrorCode,0,(LPSTR)&locbuffer,0,nullptr);if(locbuffer){if(count){int c;int back =0;//// strip any trailing "\r\n"s and replace by a single "\n"//while(((c =*CharPrevA(locbuffer, locbuffer + count))=='\r')||(c =='\n')){
count--;
back++;}if(back){
locbuffer[count++]='\n';
locbuffer[count]='\0';}Message="Error: ";Message+= locbuffer;}LocalFree(locbuffer);}else{Message="Unknown error code: "+ to_string(ErrorCode);}}
Masalah dengan implementasi ini: 1Tidak ada dukungan Unicode. 2Format pesan kesalahan yang tidak sesuai. Jika penelepon perlu memproses string yang dikembalikan, itu bisa dilakukan. Implementasi Anda membuat penelepon tidak memiliki opsi. 3Penggunaan pengecualian tetapi tidak memiliki keamanan pengecualian yang tepat. Dalam hal std::stringoperator mengeluarkan pengecualian, buffer yang dialokasikan oleh FormatMessagebocor. 4Mengapa tidak mengembalikan saja std::stringalih - alih meminta penelepon melewati objek dengan referensi?
IInspectable
1
Karena c ++ 11, Anda dapat menggunakan pustaka standar alih-alih FormatMessage:
#include<system_error>
std::string GetLastErrorAsString(){
DWORD errorMessageID =::GetLastError();if(errorMessageID ==0){return std::string();//No error message has been recorded}else{return std::system_category().message(errorMessageID);}}
Hanya ada jendela kecil di mana panggilan GetLastErrormenghasilkan hasil yang bermakna. Dengan ini menjadi C ++, satu-satunya pilihan yang aman di sini adalah membuat pemanggil memberikan kode kesalahan terakhir. Itu tentu tidak membantu, bahwa kode yang disajikan panggilan GetLastErrordua kali . Selain itu, walaupun nyaman, kurangnya dukungan karakter yang luas dari C ++ 'gagal membuat error_categoryantarmuka bermanfaat secara universal. Ini hanya menambah sejarah panjang peluang C + yang terlewatkan.
IInspectable
Poin bagus tentang panggilan tak berguna ke GetLastError. Tapi saya tidak melihat perbedaan antara menelepon ke GetLastErrorsini atau meminta penelepon memanggilnya. Mengenai C ++ dan wchar: Jangan mengabaikan harapan, Microsoft mulai mengizinkan aplikasi hanya menjadi UTF-8 .
Chronial
"Saya melihat ada perbedaan" - Pertimbangkan situs panggilan berikut: log_error("error", GetLastErrorAsString());. Juga pertimbangkan bahwa log_errorargumen pertama adalah tipe std::string. Panggilan panggilan konversi (tidak terlihat) baru saja menjatuhkan jaminan Anda untuk mendapatkan nilai yang berarti dari GetLastErrorsaat Anda menyebutnya.
IInspectable
Mengapa Anda berpikir bahwa konstruktor std :: string memanggil fungsi Win32?
Kronologis
1
mallocpanggilan HeapAllocuntuk tumpukan proses. Tumpukan proses bisa ditumbuhkan. Jika perlu tumbuh, pada akhirnya akan memanggil VirtualAlloc , yang tidak mengatur kode kesalahan terakhir thread panggilan. Nah, itu benar-benar kehilangan intinya, yaitu: C ++ adalah ladang ranjau. Implementasi ini hanya menambah itu, dengan menyediakan antarmuka dengan jaminan tersirat itu tidak bisa hidup sampai. Jika Anda yakin tidak ada masalah, mudah bagi Anda untuk membuktikan kebenaran solusi yang diajukan. Semoga berhasil.
IInspectable
0
Saya akan meninggalkan ini di sini karena saya harus menggunakannya nanti. Ini adalah sumber untuk alat kompatibel biner kecil yang akan bekerja sama baiknya di assembly, C dan C ++.
GetErrorMessageLib.c (dikompilasi ke GetErrorMessageLib.dll)
#include<Windows.h>/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/
__declspec(dllexport)intGetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes){
LPSTR tmp;
DWORD result_len;
result_len =FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPSTR)&tmp,0,
NULL
);if(result_len ==0){return-1;}// FormatMessage's return is 1 character too short.++result_len;
strncpy(lpResult, tmp, dwBytes);
lpResult[dwBytes -1]=0;LocalFree((HLOCAL)tmp);if(result_len <= dwBytes){return0;}else{return result_len;}}/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/
__declspec(dllexport)intGetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes){
LPWSTR tmp;
DWORD nchars;
DWORD result_bytes;
nchars = dwBytes >>1;
result_bytes =2*FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPWSTR)&tmp,0,
NULL
);if(result_bytes ==0){return-1;}// FormatMessage's return is 1 character too short.
result_bytes +=2;
wcsncpy(lpResult, tmp, nchars);
lpResult[nchars -1]=0;LocalFree((HLOCAL)tmp);if(result_bytes <= dwBytes){return0;}else{return result_bytes *2;}}
versi sebaris (GetErrorMessage.h):
#ifndefGetErrorMessage_H#defineGetErrorMessage_H#include<Windows.h>/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/staticinlineintGetErrorMessageA(DWORD dwErrorCode, LPSTR lpResult, DWORD dwBytes){
LPSTR tmp;
DWORD result_len;
result_len =FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPSTR)&tmp,0,
NULL
);if(result_len ==0){return-1;}// FormatMessage's return is 1 character too short.++result_len;
strncpy(lpResult, tmp, dwBytes);
lpResult[dwBytes -1]=0;LocalFree((HLOCAL)tmp);if(result_len <= dwBytes){return0;}else{return result_len;}}/***
* returns 0 if there was enough space, size of buffer in bytes needed
* to fit the result, if there wasn't enough space. -1 on error.
*/staticinlineintGetErrorMessageW(DWORD dwErrorCode, LPWSTR lpResult, DWORD dwBytes){
LPWSTR tmp;
DWORD nchars;
DWORD result_bytes;
nchars = dwBytes >>1;
result_bytes =2*FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
dwErrorCode,
LANG_SYSTEM_DEFAULT,(LPWSTR)&tmp,0,
NULL
);if(result_bytes ==0){return-1;}// FormatMessage's return is 1 character too short.
result_bytes +=2;
wcsncpy(lpResult, tmp, nchars);
lpResult[nchars -1]=0;LocalFree((HLOCAL)tmp);if(result_bytes <= dwBytes){return0;}else{return result_bytes *2;}}#endif/* GetErrorMessage_H */
usecase dinamis (diasumsikan kode kesalahan valid, jika tidak diperlukan -1 pemeriksaan):
Ini benar-benar tidak menambahkan sesuatu yang bermanfaat. Di atas semua itu, ia menyebut versi ANSI FormatMessage, tanpa alasan yang jelas, dan secara sewenang-wenang membatasi diri hingga 80 karakter, sekali lagi, tanpa alasan apa pun. Saya khawatir, ini tidak membantu.
IInspectable
Anda benar saya berharap tidak ada yang akan memperhatikan tentang kurangnya versi Unicode. Saya akan memeriksa cara mendefinisikan string Unicode di gnu sebagai dan memodifikasi solusi saya. maaf tentang ketidakjujuran.
Dmitry
ok versi unicode sudah habis. dan itu bukan alasan sewenang-wenang; semua pesan kesalahan kurang dari 80 karakter, atau tidak layak dibaca, dan kode kesalahan lebih penting daripada pesan kesalahan. Tidak ada pesan kesalahan standar yang melebihi 80 karakter sehingga asumsi yang aman, dan ketika tidak, tidak bocor memori.
Dmitry
3
"semua pesan kesalahan kurang dari 80 karakter, atau tidak layak dibaca" - Itu salah. Pesan kesalahan untuk ERROR_LOCK_VIOLATION(33) adalah: "Proses tidak dapat mengakses file karena proses lain telah mengunci sebagian file." Keduanya jelas lebih panjang dari 80 karakter, dan sangat layak dibaca, jika Anda mencoba menyelesaikan masalah dan menemukannya dalam file log diagnostik. Jawaban ini tidak menambah nilai substansial atas jawaban yang ada.
IInspectable
Ini tidak kalah naif. Itu lebih jarang gagal. Kecuali untuk implementasi perakitan yang terletak tentang ukuran buffer (mengklaim memiliki ruang untuk 200 karakter, ketika hanya memiliki ruang untuk 100). Sekali lagi, ini tidak menambahkan sesuatu yang substansial, itu belum ada di jawaban lain. Secara khusus, ini lebih buruk daripada jawaban ini . Lihat daftar peluru di sana dan amati, masalah apa yang telah Anda implementasikan, di atas yang saya tunjukkan.
@err,hr
arloji, dan minta debugger secara otomatis mengonversi kode kesalahan terakhir ke representasi yang dapat dibaca manusia. Penentu,hr
format bekerja untuk setiap ekspresi yang mengevaluasi ke nilai integral, misalnya5,hr
arloji akan menampilkan "ERROR_ACCESS_DENIED: Akses ditolak." .GetLastError()
dokumentasi: " Untuk mendapatkan string kesalahan untuk kode kesalahan sistem, gunakanFormatMessage()
fungsinya. ". Lihat contoh Mengambil Kode Kesalahan Terakhir di MSDN.Jawaban:
sumber
(LPSTR)&messageBuffer
dalam kasus ini, karena jika tidak, FormatMessageA tidak dapat mengubah nilainya untuk menunjuk ke buffer yang dialokasikan.strsafe.h
header, yang tidak aman sama sekali, itu menyebabkan banyak kesalahan kompiler diwinuser.h
danwinbase.h
.1
GetLastError
berpotensi dipanggil terlambat.2
Tidak ada dukungan Unicode.3
Penggunaan pengecualian tanpa menerapkan jaminan keamanan pengecualian.Diperbarui (11/2017) untuk mempertimbangkan beberapa komentar.
Contoh mudah:
sumber
1
Kegagalan untuk menentukanFORMAT_MESSAGE_IGNORE_INSERTS
bendera penting .2
GetLastError
berpotensi dipanggil terlambat.3
Pembatasan pesan yang sewenang-wenang menjadi 256 unit kode.4
Tidak ada penanganan kesalahan.256
bukan(sizeof(buf) / sizeof(wchar_t)
? apakah itu dapat diterima dan aman?MSDN memiliki beberapa kode sampel yang menunjukkan cara menggunakan
FormatMessage()
danGetLastError()
bersama - sama: Mengambil Kode Kesalahan Terakhirsumber
FormatMessage akan mengubah integer GetLastError menjadi pesan teks.
sumber
GetLastError mengembalikan kode kesalahan numerik. Untuk mendapatkan pesan kesalahan deskriptif (misalnya, untuk ditampilkan kepada pengguna), Anda dapat memanggil FormatMessage :
Di C ++, Anda dapat menyederhanakan antarmuka dengan menggunakan kelas std :: string:
CATATAN: Fungsi-fungsi ini juga berfungsi untuk nilai HRESULT. Cukup ubah parameter pertama dari DWORD dwErrorCode ke HRESULT hResult. Sisa kode dapat tetap tidak berubah.
Implementasi ini memberikan peningkatan berikut atas jawaban yang ada:
FORMAT_MESSAGE_IGNORE_INSERTS
bendera dengan benar. Lihat Pentingnya bendera FORMAT_MESSAGE_IGNORE_INSERTS untuk informasi lebih lanjut.Jawaban ini telah dimasukkan dari Dokumentasi Stack Overflow. Pengguna berikut telah berkontribusi pada contoh: stackptr , Ajay , Cody Grey ♦ , IInspectable .
sumber
std::runtime_error
, saya menyarankan untuk melempar distd::system_error(lastError, std::system_category(), "Failed to retrieve error message string.")
manalastError
akan menjadi nilai balikGetLastError()
setelahFormatMessage()
panggilan gagal .FormatMessage
secara langsung?::HeapFree(::GetProcessHeap(), 0, p)
dalam deleter, bukan::LocalFree(p)
seperti yang ditunjukkan oleh dokumentasi? 2) Saya menyadari bahwa dokumentasi mengatakan untuk melakukannya dengan cara ini, tetapi tidakreinterpret_cast<LPTSTR>(&psz)
melanggar aturan aliasing yang ketat?HeapFree
itu diperkenalkan, sementara ini masih menjadi topik dalam Dokumentasi SO. Saya harus menyelidikinya (walaupun dokumentasinya tampak jelas, bahwa ini tidak aman). Pertanyaan 2): Ini dua kali lipat. Untuk konfigurasi MBCS,LPTSTR
adalah alias untukchar*
. Para pemain selalu aman. Untuk konfigurasi Unicode, casting kewchar_t*
adalah mencurigakan pada tingkat apa pun. Saya tidak tahu apakah ini aman di C, tetapi kemungkinan besar tidak di C ++. Saya harus menyelidiki ini juga.Secara umum, Anda perlu menggunakan
FormatMessage
untuk mengkonversi dari kode kesalahan Win32 ke teks.Dari dokumentasi MSDN :
Deklarasi FormatMessage:
sumber
Jika Anda menggunakan c # Anda dapat menggunakan kode ini:
sumber
Jika Anda perlu mendukung MBCS dan juga Unicode, jawaban Mr.C64 tidak cukup. Buffer harus dinyatakan TCHAR, dan dilemparkan ke LPTSTR. Perhatikan bahwa kode ini tidak berurusan dengan baris baru yang menjengkelkan yang ditambahkan Microsoft ke pesan kesalahan.
Juga, untuk singkatnya saya menemukan metode berikut berguna:
sumber
CString
c'tor melempar pengecualian, implementasi ini bocor memori yang dialokasikan oleh panggilan keFormatMessage
.sumber
1
Tidak ada dukungan Unicode.2
Format pesan kesalahan yang tidak sesuai. Jika penelepon perlu memproses string yang dikembalikan, itu bisa dilakukan. Implementasi Anda membuat penelepon tidak memiliki opsi.3
Penggunaan pengecualian tetapi tidak memiliki keamanan pengecualian yang tepat. Dalam halstd::string
operator mengeluarkan pengecualian, buffer yang dialokasikan olehFormatMessage
bocor.4
Mengapa tidak mengembalikan sajastd::string
alih - alih meminta penelepon melewati objek dengan referensi?Karena c ++ 11, Anda dapat menggunakan pustaka standar alih-alih
FormatMessage
:sumber
GetLastError
menghasilkan hasil yang bermakna. Dengan ini menjadi C ++, satu-satunya pilihan yang aman di sini adalah membuat pemanggil memberikan kode kesalahan terakhir. Itu tentu tidak membantu, bahwa kode yang disajikan panggilanGetLastError
dua kali . Selain itu, walaupun nyaman, kurangnya dukungan karakter yang luas dari C ++ 'gagal membuaterror_category
antarmuka bermanfaat secara universal. Ini hanya menambah sejarah panjang peluang C + yang terlewatkan.GetLastError
. Tapi saya tidak melihat perbedaan antara menelepon keGetLastError
sini atau meminta penelepon memanggilnya. Mengenai C ++ dan wchar: Jangan mengabaikan harapan, Microsoft mulai mengizinkan aplikasi hanya menjadi UTF-8 .log_error("error", GetLastErrorAsString());
. Juga pertimbangkan bahwalog_error
argumen pertama adalah tipestd::string
. Panggilan panggilan konversi (tidak terlihat) baru saja menjatuhkan jaminan Anda untuk mendapatkan nilai yang berarti dariGetLastError
saat Anda menyebutnya.malloc
panggilanHeapAlloc
untuk tumpukan proses. Tumpukan proses bisa ditumbuhkan. Jika perlu tumbuh, pada akhirnya akan memanggil VirtualAlloc , yang tidak mengatur kode kesalahan terakhir thread panggilan. Nah, itu benar-benar kehilangan intinya, yaitu: C ++ adalah ladang ranjau. Implementasi ini hanya menambah itu, dengan menyediakan antarmuka dengan jaminan tersirat itu tidak bisa hidup sampai. Jika Anda yakin tidak ada masalah, mudah bagi Anda untuk membuktikan kebenaran solusi yang diajukan. Semoga berhasil.Saya akan meninggalkan ini di sini karena saya harus menggunakannya nanti. Ini adalah sumber untuk alat kompatibel biner kecil yang akan bekerja sama baiknya di assembly, C dan C ++.
GetErrorMessageLib.c (dikompilasi ke GetErrorMessageLib.dll)
versi sebaris (GetErrorMessage.h):
usecase dinamis (diasumsikan kode kesalahan valid, jika tidak diperlukan -1 pemeriksaan):
kasus penggunaan reguler (menganggap kode kesalahan valid, jika tidak -1 pemeriksaan kembali diperlukan):
contoh menggunakan dengan assembly gnu seperti pada MinGW32 (sekali lagi, diasumsikan kode kesalahan valid, jika tidak diperlukan -1 cek).
hasil:
The process cannot access the file because another process has locked a portion of the file.
sumber
FormatMessage
, tanpa alasan yang jelas, dan secara sewenang-wenang membatasi diri hingga 80 karakter, sekali lagi, tanpa alasan apa pun. Saya khawatir, ini tidak membantu.ERROR_LOCK_VIOLATION
(33) adalah: "Proses tidak dapat mengakses file karena proses lain telah mengunci sebagian file." Keduanya jelas lebih panjang dari 80 karakter, dan sangat layak dibaca, jika Anda mencoba menyelesaikan masalah dan menemukannya dalam file log diagnostik. Jawaban ini tidak menambah nilai substansial atas jawaban yang ada.