How do I call WSAGetLastError() from WinAPI so I get the valid text error?
[DllImport("ws2_32.dll", CharSet = CharSet.Auto, SetLastError = true)]
static extern Int32 WSAGetLastError();
Also, on pinvoke.net it's said:
You should never PInvoke to GetLastError. Call Marshal.GetLastWin32Error instead!
System.Runtime.InteropServices.Marshal.GetLastWin32Error()
WSAGetLastError is just a wrapper for the Win32 GetLastError function.
If you're doing things with P/Invoke, you can use the SetLastError parameter to the DllImport attribute. It tells .NET that the imported function will call SetLastError(), and that the value should be collected.
If the imported function fails, you can get at the last error with Marshal.GetLastWin32Error(). Alternatively, you can just throw new Win32Exception(), which uses this value automatically.
If you're not doing things with P/Invoke, you're out of luck: there's no guarantee that the last error value will be preserved long enough to make it back through multiple layers of .NET code. In fact, I'll link to Adam Nathan: never define a PInvoke signature for GetLastError.
It doesn't make very much sense to call that function from managed code. It makes sense in unmanaged code because you know the exact last Win32 function that was called, so you know what function must have set the last error. In managed code, you don't know what functions have been called.
You could probably use P/Invoke to call the function; it just wouldn't do you any good. What are you trying to accomplish?
This is how I saw on the web to put GetLastError into the C# exception mechanismand how to get it back out again...
try
{
// some p/invoke call that is going to fail with a windows error ...
mHndActivatedDevice = MyNameSpace.Interop.Device.Device.ActivateDevice(
"Drivers\\BuiltIn\\SomeDriverName", IntPtr.Zero, 0, IntPtr.Zero);
}
catch(System.ComponentModel.Win32Exception exc) // as suggested by John Saunders
{
// you can get the last error like this:
int lastError = System.Runtime.InteropServices.Marshal.GetLastWin32Error();
Console.WriteLine("error:" + lastError.ToString());
// but it is also inside the exception, you can get it like this
Console.WriteLine(exc.NativeErrorCode.ToString());
Console.WriteLine(exc.ToString());
}
where ActivateDevice is defined thus:
Related
Suppose we have the following C++ code:
typedef int (*getIntPtr)(void);
extern "C" __declspec(dllexport) void InvokeFuncPtr(getIntPtr funcPtr) {
std::wcout << funcPtr();
}
We can match this definition in C#:
[DllImport("NativeLib.dll", CallingConvention = CallingConvention.Cdecl), SuppressGCTransition]
public static unsafe extern void InvokeFuncPtr(delegate* unmanaged[Cdecl]<int> funcPtr);
And then we can use this function like so:
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvCdecl) })]
public static int ReturnInt() => 123;
// ... Elsewhere:
unsafe {
InvokeFuncPtr(&ReturnInt);
}
When InvokeFuncPtr is marked with SuppressGCTransition this results in the program crashing with the error "Fatal error. Invalid Program: attempted to call a UnmanagedCallersOnly method from managed code.". If we remove the SuppressGCTransition attribute it works as expected, and 123 is printed to the console.
Is this expected behaviour? I imagine it amounts to the runtime seeing ReturnInt() as being invoked from managed code, simply with a couple of extra steps of indirection. If so, is there any way around this, or should I simply leave the SuppressGCTransition attribute off?
Yes, you should leave off SuppressGCTransition, as the stack is what the runtime uses to identify whether the caller is managed or unmanaged.
If there is no transition in the stack, there is no way for the runtime to tell that the stack transitioned to unmanaged.
Alternatively, you can leave off UnmanagedCallersOnly, and instead marshal the delegate with Marshal.GetFunctionPointerForDelegate.
I am calling an imported C++ dll (a custom OpenGL window dll) function from C# using this:
[DllImport("Renderer.dll", EntryPoint = "LoadModelsAndMotions", CallingConvention = CallingConvention.Cdecl)]
public static extern void LoadModelsAndMotions(string[] modelFiles, int nModels, string[] motionFiles, int nMotions);
I have about four or five functions which I use. The only problem is this function when called results in the next line of code in my c# app never to be reached.
If I run it inline with my code, my UI locks up until it is complete but the code after is not executed.
If I run it in an async task the code after this call in the task is never reached.
If anyone has any ideas why this would happen?
I'd suggest checking whether the signature of the imported method is right (if it's really string[] parameter, look at MarshalAsAttribute). You can also try adding SetLastError = true to the attribute, if that changes anything.
I tested a lot. But I found no disadvantages of those 2!
But see the accepted answer.
I read here that calling GetLastError in managed code is unsafe because the Framework might internally "overwrite" the last error. I have never had any noticeable problems with GetLastError and it seems for me that the .NET Framework is smart enough not to overwrite it. Therefore I have a few questions on that topic:
in [DllImport("kernel32.dll", SetLastError = true)] does the SetLastError attribute make the Framework store the error code for the use of Marshal.GetLastWin32Error() ?
is there an example where plain GetLastError fails to give the correct result ?
do I really HAVE to use Marshal.GetLastWin32Error() ?
is this "problem" Framework version related ?
public class ForceFailure
{
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
public static void Main()
{
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
System.Console.WriteLine("It worked???");
else
{
// the first last error check is fine here:
System.Console.WriteLine(GetLastError());
System.Console.WriteLine(Marshal.GetLastWin32Error());
}
}
}
Producing errors:
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
Console.WriteLine("It worked???");
else
{
// bad programming but ok GetlLastError is overwritten:
Console.WriteLine(Marshal.GetLastWin32Error());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
}
catch { }
Console.WriteLine(GetLastError());
}
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
Console.WriteLine("It worked???");
else
{
// bad programming and Marshal.GetLastWin32Error() is overwritten as well:
Console.WriteLine(GetLastError());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) { }
}
catch { }
Console.WriteLine(Marshal.GetLastWin32Error());
}
// turn off concurrent GC
GC.Collect(); // doesn't effect any of the candidates
Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(" -> " + GetLastError());
Console.WriteLine(Marshal.GetLastWin32Error());
Console.WriteLine(Marshal.GetLastWin32Error());
// when you exchange them -> same behaviour just turned around
I don't see any difference! Both behave the same except Marshal.GetLastWin32Error stores results from App->CLR->WinApi calls as well and GetLastError stores only results from App->WinApi calls.
Garbage Collection seems not to call any WinApi functions overwriting the last error code
GetLastError is thread-safe. SetLastError stores an error code for each thread calling it.
since when would GC run in my threads ?
You must always use the Marshal.GetLastWin32Error. The main problem is the garbage collector. If it runs between the call of SetVolumeLabel and the call of GetLastError then you will receive the wrong value, because the GC has surely overwritten the last result.
Therefore you always need to specify the SetLastError=true in the DllImport-Attribute:
[DllImport("kernel32.dll", SetLastError=true)]
static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
This ensures that the marhsallling stub calls immediately after the native function the "GetLastError" and stores it in the local thread.
And if you have specified this attribute then the call to Marshal.GetLastWin32Error will always have the correct value.
For more info see also "GetLastError and managed code" by Adam Nathan.
Also other function from .NET can change the windows "GetLastError". Here is an example which produces different results:
using System.IO;
using System.Runtime.InteropServices;
public class ForceFailure
{
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool SetVolumeLabel(string lpRootPathName, string lpVolumeName);
public static void Main()
{
if (SetVolumeLabel("XYZ:\\", "My Imaginary Drive "))
System.Console.WriteLine("It worked???");
else
{
System.Console.WriteLine(Marshal.GetLastWin32Error());
try
{
using (new FileStream("sdsdafsdfsdfs sdsd ", FileMode.Open)) {}
}
catch
{
}
System.Console.WriteLine(GetLastError());
}
}
}
Also it seems that this is depended on the CLR which you are using! If you compile this with .NET2, it will produce "2 / 0"; if you switch to .NET 4, it will output "2 / 2"...
So it is depended on the CLR version, but you should not trust the native GetLastError function; always use the Marshal.GetLastWin32Error.
TL;DR
Do use [DllImport(SetLastError = true)] and Marshal.GetLastWin32Error()
perform the Marshal.GetLastWin32Error() immediately after a failing Win32 call and on the same thread.
Argumentation
As i read it, the official explanation why you need Marshal.GetLastWin32Error can be found here:
The common language runtime can make internal calls to APIs that overwrite the GetLastError maintained by the operating system.
To say it in other words:
Between your Win32 call which sets the error, the CLR may "insert" other Win32 calls which could overwrite the error.
Specifying [DllImport(SetLastError = true)] makes sure that the CLR retrieves the error code before the CLR executes any unexpected Win32 calls.
To access that variable we need to use Marshal.GetLastWin32Error.
Now what #Bitterblue found is that these "inserted calls" don't happen often - he couldn't find any. But that's not really surpising. Why? Because it's extremely difficult to "black box test" whether GetLastError works reliably:
you can detect unreliability only if a CLR-inserted Win32 call actually fails in the meantime.
failure of these calls may be dependent on internal/external factors. Such as time/timing, memory pressure, devices, state of computer, windows version...
insertion of Win32 calls by CLR may be dependent on external factors. So under some circumstances the CLR inserts a Win32 call, under others it doesn't.
behavior can change with different CLR versions as well
There's is one specific component - the Garbage collector (GC) - which is known to interrupt a .net thread if there's memory pressure and do some processing on that thread (see What happens during a garbage collection). Now if the GC were to execute a failing Win32 call, this would break your call to GetLastError.
To sum it up, you have a plethora of unknown factors which can influence the reliability of GetLastError. You'll most likely not find an unreliability problem when developing/testing, but it might blow up in production at any time. So do use [DllImport(SetLastError = true)] and Marshal.GetLastWin32Error() and improve your sleep quality ;-)
in [DllImport("kernel32.dll", SetLastError = true)] does the SetLastError attribute make the Framework store the error code for the use of Marshal.GetLastWin32Error() ?
Yes, as is documented in DllImportAttribute.SetLastError Field
is there an example where plain GetLastError fails to give the correct result ?
As documented in Marshal.GetLastWin32Error Method, if the framework itself (e.g. the garbage collector) calls any native method that sets an error value between your calls to the native method and GetLastError you would get the error value of the framework's call instead of your call.
do I really HAVE to use Marshal.GetLastWin32Error() ?
Since you can't ensure that the framework will never call a native method between your call and the call to GetLastError, yes. Also, why not?
is this "problem" Framework version related ?
It could definitely be (e.g. changes in the garbage collector), but it doesn't have to.
I am trying to make a wrapper for a native c++ .dll using P/Invoke.
The source code for the .dll has the following entry point specified:
// .h-file
CHROMAPRINT_API ChromaprintContext *chromaprint_new(int algorithm);
And the method implementation:
// .cpp-file
ChromaprintContext *chromaprint_new(int algorithm)
{
ChromaprintContextPrivate *ctx = new ChromaprintContextPrivate();
ctx->algorithm = algorithm;
ctx->fingerprinter = new Fingerprinter(CreateFingerprinterConfiguration(algorithm));
return (ChromaprintContext *)ctx;
}
The ChromaprintContextPrivate type is a structure:
>// .cpp-file
struct ChromaprintContextPrivate {
int algorithm;
Fingerprinter *fingerprinter;
vector<int32_t> fingerprint;
};
My C# wrapper code:
// .cs-file
[System.Runtime.InteropServices.DllImportAttribute(
"libchromaprint.dll",
EntryPoint = "chromaprint_new")]
private static extern System.IntPtr chromaprint_new(int algorithm);
public static IntPtr Chromaprint_New(ChromaprintAlgorithm algorithm)
{
// Hardcoded parameter for testing
return chromaprint_new(0); // (int)algorithm
}
Calling IntPtr ptr = Chromaprint_New(0); raises the following MDA exception:
A call to PInvoke function 'MyProject.ChromaprintWrapper!'MyProject.ChromaprintWrapper.LibChromaPrint::chromaprint_new' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.
So I understand what the problem is (the number of entries on the stack isn't what is expected). I'm assuming the method parameter int algorithm is ok. I'm not sure about the return type though. Should it be a structure instead of a pointer?
I got the C# code above by running the .h-file through P/Invoke Interop Assistant. Is the return type wrong? what should it be?
What is the C# representation of vector<int32_t> fingerprint;?
(See ChromaprintContextPrivate structure above.)
You most likely need to specify the calling convention.
Try the following:
[System.Runtime.InteropServices.DllImportAttribute("libchromaprint.dll",
EntryPoint = "chromaprint_new",
CallingConvention=CallingConvention.Cdecl)]
By default, this uses Winapi (which is effectively StdCall) to make it easier to call into the Windows API, but this is not typically the default on most C++ libraries.
It's because of how the parameters are actually passed, the calling convention.
Try
[DllImport("libchromaprint.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern IntPtr chromaprint_new(int algorithm);
You need to declare cdecl calling convention on the dllimport attribute.
this is my code
public delegate void NotifyFunc(enumType notificationType, IntPtr data);
[DllImport("VssSdkd")]
public static extern void startVssSdkClientEcho(string IpAddress,
long port, NotifyFunc notifyFunc, eProtocolType proType, bool Req);
am call the function like
CallBack calbck = new CallBack(TestDllImport.Callbackas);
startVssSdkClientEcho("192.168.10.240", 12050, calbck, TestDllImport.eProtocolType.eProtocolType_VssSdk, false);
here my receving callback function is
static public void Callbackas(eNotificationType type, IntPtr datas) {}
here first time am receving data and then it's giving Error msg like :
run time check failure #0 - the value if ESP was not properly saved across a function call.
this is usually a result of calling a function declared with one calling convention
with a function pointer declared with a different calling convention
please help me to identify my error... thanks in advance
Is this your code you are calling (sorry cursory search could only find your other post referring to startVssSdkClientEcho). Is it decalred cdecl (i.e. it has no __declspec at all) or is it stdcall. If it is cdecl you need to add CallingConvention attribute to the DllImport
i.e.
[DllImport("VssSdkd", CallingConvention=CallingConvention.Cdecl)]
Of course it might also be the callback calling convention at fault which is another issue.