using System;
using System.Collections.Generic;
using System.Text;
using Vanara.InteropServices;
namespace Vanara.PInvoke
{
/// Generic functions to help with standard function patterns like getting a string from a method.
public static class FunctionHelper
{
private static readonly List buffErrs = new() { Win32Error.ERROR_MORE_DATA, Win32Error.ERROR_INSUFFICIENT_BUFFER, Win32Error.ERROR_BUFFER_OVERFLOW };
/// Delegate to get the size of memory allocated to a pointer.
/// The type of the size result. This is usually or .
/// The pointer to the memory in question.
/// The resulting size.
/// Resulting error or on success.
public delegate Win32Error PtrFunc(IntPtr ptr, ref TSize sz) where TSize : struct, IConvertible;
/// Delegate that takes and StringBuilder and initial size and returns a result.
/// The type of the size result. This is usually or .
/// The return type.
/// The value.
/// On input, the size of the capacity. On output, the number of characters written.
/// The return value. Often this is an error.
public delegate TRet SBFunc(StringBuilder sb, ref TSize sz) where TSize : struct, IConvertible;
/// Delegate that takes and StringBuilder and initial size and returns a result.
/// The type of the first parameter.
/// The type of the size result. This is usually or .
/// The return type.
/// The first parameter.
/// The value.
/// On input, the size of the capacity. On output, the number of characters written.
/// The return value. Often this is an error.
public delegate TRet SBFunc(T1 arg1, StringBuilder sb, ref TSize sz) where TSize : struct, IConvertible;
/// Delegate that takes and StringBuilder and initial size and returns a result.
/// The type of the first parameter.
/// The type of the second parameter.
/// The type of the size result. This is usually or .
/// The return type.
/// The first parameter.
/// The second parameter.
/// The value.
/// On input, the size of the capacity. On output, the number of characters written.
/// The return value. Often this is an error.
public delegate TRet SBFunc(T1 arg1, T2 arg2, StringBuilder sb, ref TSize sz) where TSize : struct, IConvertible;
/// Gets a size and returns an error.
/// The type of the size result. This is usually or .
/// On input, the size of the capacity. On output, the number of characters written.
/// Resulting error or on success.
public delegate Win32Error SizeFunc(ref TSize sz) where TSize : struct, IConvertible;
/// Converts a bool to a Win32Error.
/// Result state.
/// Resulting error or on success.
public static Win32Error BoolToLastErr(bool result) => result ? Win32Error.ERROR_SUCCESS : Win32Error.GetLastError();
/// Calls a method with and gets the resulting string or error.
/// The type of the size result. This is usually or .
/// The return type.
/// The lambda or method to call into.
/// The resulting string value.
/// An optional method to determine if a valid size was retrieved by .
/// The return value of .
public static TRet CallMethodWithStrBuf(SBFunc method, out string result, Func gotGoodSize = null) where TSize : struct, IConvertible
{
TSize sz = default;
var ret0 = method(null, ref sz);
if (!(gotGoodSize ?? IsNotDef)(sz, ret0))
{
result = null;
return ret0;
}
var len = sz.ToInt32(null) + 1;
var sb = new StringBuilder(len, len);
sz = (TSize)Convert.ChangeType(len, typeof(TSize));
var ret = method(sb, ref sz);
result = sb.ToString();
return ret;
}
/// Calls a method with and gets the resulting string or error.
/// The type of the first parameter.
/// The type of the size result. This is usually or .
/// The return type.
/// The lambda or method to call into.
/// The first parameter.
/// The resulting string value.
/// An optional method to determine if a valid size was retrieved by .
/// The return value of .
public static TRet CallMethodWithStrBuf(SBFunc method, T1 arg1, out string result,
Func gotGoodSize = null) where TSize : struct, IConvertible
{
TSize sz = default;
var ret0 = method(arg1, null, ref sz);
if (!(gotGoodSize ?? IsNotDef)(sz, ret0))
{
result = null;
return ret0;
}
var len = sz.ToInt32(null) + 1;
var sb = new StringBuilder(len, len);
sz = (TSize)Convert.ChangeType(len, typeof(TSize));
var ret = method(arg1, sb, ref sz);
result = sb.ToString();
return ret;
}
/// Calls a method with and gets the resulting string or error.
/// The type of the first parameter.
/// The type of the second parameter.
/// The type of the size result. This is usually or .
/// The return type.
/// The lambda or method to call into.
/// The first parameter.
/// The second parameter.
/// The resulting string value.
/// An optional method to determine if a valid size was retrieved by .
/// The return value of .
public static TRet CallMethodWithStrBuf(SBFunc method, T1 arg1, T2 arg2, out string result,
Func gotGoodSize = null) where TSize : struct, IConvertible
{
TSize sz = default;
var ret0 = method(arg1, arg2, null, ref sz);
if (!(gotGoodSize ?? IsNotDef)(sz, ret0))
{
result = null;
return ret0;
}
var len = sz.ToInt32(null) + 1;
var sb = new StringBuilder(len, len);
sz = (TSize)Convert.ChangeType(len, typeof(TSize));
var ret = method(arg1, arg2, sb, ref sz);
result = sb.ToString();
return ret;
}
/// Calls a method with and gets the resulting string or error.
/// The type of the size result. This is usually or .
/// The return type.
/// The lambda or method to call into.
/// The size value to pass into .
/// The resulting string value.
/// The return value of .
public static TRet CallMethodWithStrBuf(Func method, TSize bufSz, out string result) where TSize : IConvertible
{
var len = bufSz.ToInt32(null) + 1;
var sb = new StringBuilder(len, len);
var ret = method(sb, bufSz);
result = sb.ToString();
return ret;
}
/// Calls a method with buffer for a type and gets the result or error.
/// The return type.
/// The type of the size result. This is usually or .
/// Method to get the size of the buffer.
/// The lambda or method to call into.
/// The resulting value of .
///
/// An optional method to convert the pointer to the type specified by . By default, this will marshal the
/// pointer to the structure.
///
///
/// The optional error returns when the buffer size is insufficient. If left , then a
/// list of well known errors will be used.
///
/// Resulting error or on success.
public static Win32Error CallMethodWithTypedBuf(SizeFunc getSize, PtrFunc method, out TOut result,
Func outConverter = null, Win32Error? bufErr = null) where TSize : struct, IConvertible
{
TSize sz = default;
result = default;
var err = (getSize ?? GetSize)(ref sz);
if (err.Failed && (bufErr == null || bufErr.Value != err) && !buffErrs.Contains(err)) return err;
using (var buf = new SafeHGlobalHandle(sz.ToInt32(null)))
{
err = method(buf.DangerousGetHandle(), ref sz);
if (err.Succeeded)
result = (outConverter ?? Conv)(buf.DangerousGetHandle(), sz);
return err;
}
Win32Error GetSize(ref TSize sz1) => method(IntPtr.Zero, ref sz1);
static TOut Conv(IntPtr p, TSize s) => p == IntPtr.Zero ? default : p.Convert(Convert.ToUInt32(s));
}
/// Calls a method with buffer for a type and gets the result or error.
/// The return type.
/// The type of the size result. This is usually or .
/// The lambda or method to call into.
/// Method to get the size of the buffer.
///
/// An optional method to convert the pointer to the type specified by . By default, this will marshal the
/// pointer to the structure.
///
///
/// The optional error returns when the buffer size is insufficient. If left , then a
/// list of well known errors will be used.
///
/// The resulting value of .
public static TOut CallMethodWithTypedBuf(PtrFunc method, SizeFunc getSize = null,
Func outConverter = null, Win32Error? bufErr = null) where TSize : struct, IConvertible
{
CallMethodWithTypedBuf(getSize, method, out var res, outConverter, bufErr).ThrowIfFailed();
return res;
}
/// Checks to see if size is not 0 and if the error is requesting a larger buffer.
/// The type of the size result. This is usually or .
/// The error provider return type.
/// On input, the size of the capacity. On output, the number of characters written.
/// The error.
/// true if buffer size is good; otherwise false.
public static bool ChkGoodBuf(TSize sz, TRet err) where TSize : struct where TRet : IErrorProvider, IConvertible =>
!sz.Equals(default(TSize)) && buffErrs.ConvertAll(e => (HRESULT)e).Contains(err.ToHRESULT());
private static bool IsNotDef(TSize _sz, TRet _ret) where TSize : struct, IConvertible => !_sz.Equals(default(TSize));
/*
public delegate TRet P0QI(in Guid iid, out object ppv) where TRet : IErrorProvider, IConvertible;
public delegate TRet P1QI(TIn1 p1, in Guid iid, out object ppv) where TRet : IErrorProvider, IConvertible;
public delegate TRet P1IQI(in TIn1 p1, in Guid iid, out object ppv) where TIn1 : struct where TRet : IErrorProvider, IConvertible;
public delegate TRet P2QI(TIn1 p1, TIn2 p2, in Guid iid, out object ppv) where TRet : IErrorProvider, IConvertible;
public static TIntf QueryInterface(P0QI f) => f0(typeof(TIntf).GUID, out object ppv).Succeeded ? (TIntf)ppv : throw new InvalidCastException();
public static TIntf QueryInterface(P1QI f, TIn1 p1) => QueryInterface(f1, p1, hr => hr.Succeeded);
public static TIntf QueryInterface(P1IQI f, in TIn1 p1) where TIn1 : struct => QueryInterface(f1, in p1, hr => hr.Succeeded);
private void Test()
{
static HRESULT GetObj(uint f, in Guid iid, out object ppv);
X x = QueryInterface(GetObj, 3U);
}
*/
}
}