Constraints vs abstract class using SafeHandle - c#

There is a method within BCryptNative called GetInt32Property.
It has the following signature:
internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle
This method only works when T is of type SafeBCryptAlgorithmHandle or SafeBCryptHashHandle. It calls native methods which are explicitly defined with those types of handles:
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
Microsoft uses function pointers / delegates to point to the correct native function. My question is, why didn't Microsoft just implemented the GetInt32Property method with the following signature:
internal static int GetInt32Property(SafeHandle algorithm, string property)
with the following native method:
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetProperty(SafeHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
Are there any downsides to this? (assuming that the SafeHandle passed to GetInt32Property is always either a SafeBCryptAlgorithmHandle or SafeBCryptHashHandle).
I'm just wondering about why Microsoft implemented this so relatively complicated.
Does it have to with:
Security-Transparent Code?
Type safety? (So that you never use any other than those two types)
Is it allowed to use SafeHandle explicitly?
According to the documentation the class must be inherited, and it is, however does a P/Invoked function handle it properly when given an abstract class of SafeHandle? Does it increment and decrement the reference counts appropriately?

It's hard to tell why Microsoft chose to implement something in one way or another, but I can answer your points.
The code isn't complex. The usage is clear (something like GetInt32Property(algorithm, str).
It doesn't force you to send one of the types you mentioned, you can still call it with a different class, as long as it implements SafeHandle.
The native methods that are used are actually the same. This is kind of odd, but I'm not an expert on this specific library so it may be for a good reason.
There's a hidden benefit for generic methods like this one. The typeof(T) == typeof(SafeBCryptHashHandle) type checks are not done in runtime, but in JIT time. This means that the method should perform slightly faster then a regular runtime check like algorith is SafeBCrypthHashHandle.
The SafeHandle class is an abstract class. This means that you cannot create an instance of it, but you can inherit it. The native function only get marshaled data, they don't get real references to objects. Don't worry about reference counting.

Related

Marshal [in] reference without ref

Some functions, especially in COM interfaces, expose a REFIID parameter that is used to specify the type of the interface the methods should return. Here's such an example method:
[DllImport("shell32.dll", PreserveSig=false)]
[return: MarshalAs(UnmanagedType.IUnknown)]
static extern object SHBindToObject(IShellFolder psf, IntPtr pidl, [MarshalAs(UnmanagedType.IUnknown)]object pbc, [In]ref Guid riid);
The fourth parameter is input-only, and should not be changed by SHBindToObject, so by C# conventions, it makes no sense passing it as a reference (aside from performance). I can't recall it clearly, but I remember that there should be some custom attribute or something that is designated for this case, to tell the marshaller that it should be really marshalled as if it were ref Guid, while it is specified without ref in the signature.
I looked for attributes in the System.Runtime.InteropServices names, fields on MarshalAsAttribute, and in the UnmanagedType enum, but without success.
Does there happen to be something similar, or is my memory incorrect? Is it good using such a thing in this case?
You're looking for MarshalAs(UnmanagedType.LPStruct):
[DllImport("shell32.dll")]
[return: MarshalAs(UnmanagedType.IUnknown)]
static extern object SHBindToObject(
IShellFolder psf,
IntPtr pidl,
[MarshalAs(UnmanagedType.IUnknown)] object pbc,
[MarshalAs(UnmanagedType.LPStruct)] Guid riid);

Should I use the ref keyword here?

I'm new to C# so I looked at this question but I'm still not sure whether I should be including the ref keyword here when marshalling the second parameter of the GetWindoInfo() Win32 API call using p/invoke:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", EntryPoint = "GetWindowInfo", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetWindowInfo(IntPtr hwnd, [MarshalAs(UnmanagedType.Struct)] ref tagWINDOWINFO pwi);
From the MSDN documentation for ref:
The ref keyword causes an argument to be passed by reference, not by
value.
So in that case my code above seems to be correct, right? Would changing the marshalling clause to instead marshal an UnmanagedType.LPStruct and removing the ref keyword result in the same thing? Like so:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", EntryPoint = "GetWindowInfo", ExactSpelling = true, CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetWindowInfo(IntPtr hwnd, [MarshalAs(UnmanagedType.LPStruct)] tagWINDOWINFO pwi);
From the MSDN documentation for GetWindowInfo function:
BOOL WINAPI GetWindowInfo(
_In_ HWND hwnd,
_Inout_ PWINDOWINFO pwi
);
EDIT:
As mentioned in the comments to the answer, the first code example is correct. The second code block is incorrect because passing by pointer and passing by reference are different and reflect a fundamental misunderstanding on my part. I was thinking of C++ dereferencing. See this question for more.
Yes, you should use the ref keyword for the struct.
First, there's a really great resource for P/Invoke definitions that you can find at www.pinvoke.net - and the particular method you're dealing with is documented here: http://www.pinvoke.net/default.aspx/user32.getwindowinfo. You'll see that it's defined using the ref keyword. This makes sense when you consider that the method is documented as _Inout_ for that parameter in the MSDN docs you linked.
The reason for this is that the ref keyword will allow that reference to be passed and have the struct you're dealing with before calling the method get updated/modified - rather than passing it in as is and never getting to see what the method actually does to it. In other words, without the ref keyword, your code would never see changes to the struct made within the GetWindowInfo call - GetWindowInfo would be working with its "own" copy of that structure.
If you want to read a bit more about why your second version wouldn't work, check out JaredPar's great explanation to What is the difference between a C# Reference and a Pointer?

When Importing a DLL Why Do Methods Have To Be Declared Static and Extern?

As the title asks, when you import a DLL such as User32.dll and declare methods to call methods on that DLL why do the methods need to be declared as Static and Extern.
I.E, this was taken from another StackOverflow answer, but demonstrates what I'm asking.
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, UIntPtr dwExtraInfo);
static - As they do not need instance of the class, those method's are call to system API and do not need any Initialization, can be used in static / non-static block
extern - The extern modifier is used to declare a method that is implemented externally. Since API methods are not declared in the class file itself, extern tell the compiler that method declared else-where.
While I do not really know whether there are technical details to this, I'd say the reasons are:
static
Methods that reside in external DLLs can not be bound to any object instance. If they were bound to an object instance, the DLL would have to track which instance is calling the method every time.
extern
I guess this is just a keyword telling the compiler to insert code for binding the DLL instead of expecting the method to be implemented in C#.

c# using const with DllImport

I have a code example from a SDK, the DLL is written in C and calls for the following:
int getAttribute(const RawDevControl *control, RawDevAttribute *attribute)
I'm using
[DllImport(#"Dev.dll",
SetLastError = true)]
internal static extern int getAttribute(const RControl *control, RAttribute *attribute);
But of course you can not use const as a type when defining this reference function.
How can I make this work with c#?
Since C# doesn't have the concept of const references, you don't really need to worry about it. On the DLL side, the code will still think you have a const pointer. Thus, your import changes to this:
[DllImport(#"Dev.dll", SetLastError = true)]
internal static extern int getAttribute(RControl control, RAttribute attribute);
This, of course, assumes that both RControl and RAttribute have been defined in C#. If they are structs, follow the examples on MSDN for defining structs for use with P/Invoke. If they are classes, that's a different set of problems. In that case, it is best if the classes are COM-based.
The declaration promises that the function doesn't alter the passed object. So take advantage of that, tell the pinvoke marshaller that there's no point in copying it back. Apply the [In] attribute:
[DllImport(#"Dev.dll", SetLastError = true)]
static extern int getAttribute([In] ref RControl control, out RAttribute attribute);
Omit the ref or out keyword if you declared the argument types as classes instead of structs.

PInvoke in 64bit .net app through c++ 64 bit dll

I'm having an issue calling a function in a c++ dll inside of a c# app. I'm calling the function inside of c# like so:
[DllImport("cryptopp.dll")]
public static extern IntPtr RSAEncryptString(string filename, string seed, string message);
It is being exported in the c++ dll as shown below.
extern "C" __declspec(dllexport) const char* __cdecl RSAEncryptString(const char *pubFilename, const char *seed, const char *message);
What I get when I try to call this, however, is an "An External component has thrown an exception." exception, which is not very descriptive at all, and extremely unhelpful.
When I pull up the dll in an export viewer, it shows all the other exported functions with fully quantified declarations (I.E. public: void __cdecl CryptoPP::X509PublicKey::`vbase destructor'(void) __ptr64 ) , except for the function I am calling, which just displays the function name RSAEncryptString.
This is the only possible issue I can see, besides maybe mis-calling the function with an invalid declaration on the c# side. Am I using System.Runtime.InteropServices.Marshal wrong?
Please help <3 and thanks in advance.
I think you need to change the first line to:
[DllImport("cryptopp.dll",
CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
If you want to get very descriptive, you can also add these:
public static extern IntPtr RSAEncryptString(
[In, MarshalAs(UnmanagedType.LPStr)] string filename,
[In, MarshalAs(UnmanagedType.LPStr)] string seed,
[In, MarshalAs(UnmanagedType.LPStr)] string message);
IIRC think the CharSet should take care of the encoding thing for you, but if it doesn't, use the MarshalAs also, as shown above.
Edit:
Oh I think I got why you still get an error! Your code still had the above problems, but it's still erring because you can't return a string object since it's not a managed object; you need to return a pointer (like IntPtr) and then use Marshal.PtrToStringAnsi!
(I didn't really look at your return type when answering this at first.)
It appears you're trying to store the return value of type const char * (an LPCSTR) into an IntPtr type (usually used for HANDLEs, not LPSTRs.) Try this:
[DllImport("cryptopp.dll", CharSet = CharSet.Auto)]
public static extern String RSAEncryptString(String filename, String seed, String message);
Also keep in mind that if any argument is getting written to, you'll need to add out before its type, i.e. ..., out String message)

Categories

Resources