Здравствуйте, Pavel Dvorkin, Вы писали:
PD>Он может там оказаться
Всё правильно.
Но, тут не PInvoke, поэтому я твои выдержки опустил, а вставлю другие из того же поста:
For managed C++ and IJW, the story is a little different. This is important for you C++ programmers out there, so I hope you're still reading! If you use DllImport explicitly in C++, the same rules apply as with C#. But when you call unmanaged APIs directly from managed C++ code, neither GetLastError nor Marshal.GetLastWin32Error will work reliably. GetLastError won't work for the same reason that a PInvoke call to it wouldn't work in C#. Marshal.GetLastWin32Error won't work because the implicit PInvoke goop emitted by the compiler doesn't set SetLastError to true. To fix this, you can use #pragma unmanaged to keep code that relies on GetLastError functionality as unmanaged code.
Именно на это я изначально и намекал, что есть какая-то магия.
Теперь о магии.
Пример 1. Упрощенный.
| Скрытый текст |
| // ConsoleApplication8CppCli.cpp : main project file.
#include "stdafx.h"
#include <windows.h>
using namespace System;
using namespace System::ComponentModel;
int main(array<System::String ^> ^args)
{
::DestroyWindow(NULL); // здесь типа не PInvoke
Win32Exception e;
Console::WriteLine(L"{0}", e.NativeErrorCode); // выводит 1400
return 0;
}
|
| |
В этом коде: не генериться ничего сверх естественного, или это трудно увидеть (в студии).
Если полазить в CLR, то в Marshal — можной найти обычный геттер, берущий last error у потока (своего).
В Win32Exception вообще специального ничего нет.
Пример 2. Отключаем магию.
| Скрытый текст |
| // ConsoleApplication8CppCli.cpp : main project file.
#include "stdafx.h"
#include <windows.h>
using namespace System;
using namespace System::ComponentModel;
#pragma managed (push, off)
void unmanaged()
{
::DestroyWindow(NULL);
}
#pragma managed (pop)
int main(array<System::String ^> ^args)
{
unmanaged(); // магия была тут
Win32Exception e;
Console::WriteLine(L"{0}", e.NativeErrorCode); // выводит 0
return 0;
}
|
| |
Осталось ещё немножко разобраться, почему это GetLastWin32Error в посте — не reliable. И если он не reliable — то как оно тогда работает, и можно ли этому доверять?
Всё до безобразия просто: в JIT / vclinker встроена эвристика, и PInvoke "goop" эмитится в флагом setlasterror для kernel32 (и что там ещё сейчас вместо модно), gdi32, user32, advapi32.
UPD:
1. Т.е. на самом деле — пока мы попадаем в эти эвристики — можно смело использовать и это будет reliable. Пост в блоге немного устарел, а этот вопрос не освещен.
2. Компилятор всё равно генерирует делает "goops" (трамплины, hunks, transitions) тем или иным способ, но неявно. Это логично и ожидаемо.
3. И уже просто в тему — забавно, но DllImport имеет разные значения по умолчанию своих параметров (например ExactSpelling) (C# vs VB.NET).