PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/Rusty/Core/PInvoke.cs

http://github.com/polyethene/IronAHK
C# | 366 lines | 254 code | 46 blank | 66 comment | 48 complexity | 862f08e963287826c4593c173fdc973b MD5 | raw file
  1. using System;
  2. using System.IO;
  3. using System.Reflection;
  4. using System.Reflection.Emit;
  5. using System.Runtime.InteropServices;
  6. namespace IronAHK.Rusty
  7. {
  8. partial class Core
  9. {
  10. /// <summary>
  11. /// Calls an unmanaged function in a DLL.
  12. /// </summary>
  13. /// <param name="function">
  14. /// <para>The path to the function, e.g. <c>C:\path\to\my.dll</c>. The ".dll" file extension can be omitted.</para>
  15. /// <para>If an absolute path is not specified on Windows the function will search the following system libraries (in order):
  16. /// User32.dll, Kernel32.dll, ComCtl32.dll, or Gdi32.dll.</para>
  17. /// </param>
  18. /// <param name="parameters">The type and argument list.</param>
  19. /// <returns>The value returned by the function.</returns>
  20. /// <remarks>
  21. /// <para><see cref="ErrorLevel"/> will be set to one of the following:</para>
  22. /// <list type="bullet">
  23. /// <item><term>0</term>: <description>success</description></item>
  24. /// <item><term>-3</term>: <description>file could not be accessed</description></item>
  25. /// <item><term>-4</term>: <description>function could not be found</description></item>
  26. /// </list>
  27. /// <para>The following types can be used:</para>
  28. /// <list type="bullet">
  29. /// <item><term><c>str</c></term>: <description>a string</description></item>
  30. /// <item><term><c>int64</c></term>: <description>a 64-bit integer</description></item>
  31. /// <item><term><c>int</c></term>: <description>a 32-bit integer</description></item>
  32. /// <item><term><c>short</c></term>: <description>a 16-bit integer</description></item>
  33. /// <item><term><c>char</c></term>: <description>an 8-bit integer</description></item>
  34. /// <item><term><c>float</c></term>: <description>a 32-bit floating point number</description></item>
  35. /// <item><term><c>double</c></term>: <description>a 64-bit floating point number</description></item>
  36. /// <item><term><c>*</c> or <c>P</c> suffix</term>: <description>pass the specified type by address</description></item>
  37. /// <item><term><c>U</c> prefix</term>: <description>use unsigned values for numeric types</description></item>
  38. /// </list>
  39. /// </remarks>
  40. public static object DllCall(object function, params object[] parameters)
  41. {
  42. ErrorLevel = 0;
  43. #region Parameters
  44. var types = new Type[parameters.Length / 2];
  45. var args = new object[types.Length];
  46. var returns = typeof(int);
  47. bool cdecl = false;
  48. for (int i = 0; i < parameters.Length; i++)
  49. {
  50. Type type = null;
  51. string name = ((string)parameters[i]).ToLowerInvariant().Trim();
  52. const string Cdecl = "cdecl";
  53. if (name.StartsWith(Cdecl))
  54. {
  55. name = name.Substring(Cdecl.Length).Trim();
  56. if (i + 1 == parameters.Length)
  57. {
  58. cdecl = true;
  59. goto returntype;
  60. }
  61. else
  62. {
  63. ErrorLevel = 2;
  64. return null;
  65. }
  66. }
  67. switch (name[name.Length - 1])
  68. {
  69. case '*':
  70. case 'P':
  71. case 'p':
  72. name = name.Substring(0, name.Length - 1).Trim();
  73. ErrorLevel = -6;
  74. // TODO: unmanaged pointers for pinvokes
  75. return null;
  76. }
  77. switch (name)
  78. {
  79. case "str": type = typeof(string); break;
  80. case "int64": type = typeof(long); break;
  81. case "uint64": type = typeof(ulong); break;
  82. case "int": type = typeof(int); break;
  83. case "uint": type = typeof(uint); break;
  84. case "short": type = typeof(short); break;
  85. case "ushort": type = typeof(ushort); break;
  86. case "char": type = typeof(char); break;
  87. case "uchar": type = typeof(char); break;
  88. case "float": type = typeof(float); break;
  89. case "double": type = typeof(double); break;
  90. default:
  91. ErrorLevel = 2;
  92. return null;
  93. }
  94. i++;
  95. if (i < parameters.Length)
  96. {
  97. int n = i / 2;
  98. types[n] = type;
  99. args[n] = Convert.ChangeType(parameters[i], type);
  100. continue;
  101. }
  102. returntype:
  103. returns = type;
  104. }
  105. #endregion
  106. #region Method
  107. #region DLL
  108. if (function is string)
  109. {
  110. string path = (string)function, name;
  111. int z = path.LastIndexOf(Path.DirectorySeparatorChar);
  112. if (z == -1)
  113. {
  114. name = path;
  115. path = string.Empty;
  116. if (Environment.OSVersion.Platform == PlatformID.Win32NT)
  117. {
  118. foreach (var lib in new[] { "user32", "kernel32", "comctl32", "gdi32" })
  119. {
  120. var handle = WindowsAPI.GetModuleHandle(lib);
  121. if (handle == IntPtr.Zero)
  122. continue;
  123. var address = WindowsAPI.GetProcAddress(handle, name);
  124. if (address == IntPtr.Zero)
  125. address = WindowsAPI.GetProcAddress(handle, name + "W");
  126. if (address != IntPtr.Zero)
  127. {
  128. path = lib + ".dll";
  129. break;
  130. }
  131. }
  132. }
  133. if (path.Length == 0)
  134. {
  135. ErrorLevel = -4;
  136. return null;
  137. }
  138. }
  139. else
  140. {
  141. z++;
  142. if (z >= path.Length)
  143. {
  144. ErrorLevel = -4;
  145. return null;
  146. }
  147. name = path.Substring(z);
  148. path = path.Substring(0, z - 1);
  149. }
  150. if (Environment.OSVersion.Platform == PlatformID.Win32NT && path.Length != 0 && !Path.HasExtension(path))
  151. path += ".dll";
  152. var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("pinvokes"), AssemblyBuilderAccess.Run);
  153. var module = assembly.DefineDynamicModule("module");
  154. var container = module.DefineType("container", TypeAttributes.Public | TypeAttributes.UnicodeClass);
  155. var invoke = container.DefinePInvokeMethod(
  156. name,
  157. path,
  158. MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.PinvokeImpl,
  159. CallingConventions.Standard,
  160. returns,
  161. types,
  162. cdecl ? CallingConvention.Cdecl : CallingConvention.Winapi,
  163. CharSet.Auto);
  164. invoke.SetImplementationFlags(invoke.GetMethodImplementationFlags() | MethodImplAttributes.PreserveSig);
  165. var created = container.CreateType();
  166. // TODO: pinvoke method caching
  167. try
  168. {
  169. var method = created.GetMethod(name);
  170. if (method == null)
  171. {
  172. ErrorLevel = -4;
  173. return null;
  174. }
  175. object value = method.Invoke(null, args);
  176. return value;
  177. }
  178. #if !DEBUG
  179. catch (Exception)
  180. {
  181. ErrorLevel = -5;
  182. return null;
  183. }
  184. #endif
  185. finally { }
  186. }
  187. #endregion
  188. #region Address
  189. else if (function is decimal || function is int || function is float)
  190. {
  191. var address = (int)function;
  192. if (address < 0)
  193. {
  194. ErrorLevel = -1;
  195. return null;
  196. }
  197. try
  198. {
  199. object value = Marshal.GetDelegateForFunctionPointer(new IntPtr(address), typeof(Delegate)).Method.Invoke(null, args);
  200. return value;
  201. }
  202. #if !DEBUG
  203. catch (Exception)
  204. {
  205. ErrorLevel = -5;
  206. return null;
  207. }
  208. #endif
  209. finally { }
  210. }
  211. #endregion
  212. #region Uknown
  213. else
  214. {
  215. ErrorLevel = -3;
  216. return null;
  217. }
  218. #endregion
  219. #endregion
  220. }
  221. /// <summary>
  222. /// Find a function in the local scope.
  223. /// </summary>
  224. /// <param name="name">The name of the function.</param>
  225. /// <returns>A delegate (function pointer).</returns>
  226. public static GenericFunction FunctionReference(string name)
  227. {
  228. var method = FindLocalMethod(name);
  229. if (method == null)
  230. return null;
  231. return (GenericFunction)Delegate.CreateDelegate(typeof(GenericFunction), method);
  232. }
  233. /// <summary>
  234. /// Returns a binary number stored at the specified address in memory.
  235. /// </summary>
  236. /// <param name="address">The address in memory.</param>
  237. /// <param name="offset">The offset from <paramref name="address"/>.</param>
  238. /// <param name="type">Any type outlined in <see cref="DllCall"/>.</param>
  239. /// <returns>The value stored at the address.</returns>
  240. public static long NumGet(long address, int offset = 0, string type = "UInt")
  241. {
  242. var adr = new IntPtr(address);
  243. char mode = type.Length > 1 ? type.ToLowerInvariant()[1] : '\0';
  244. switch (mode)
  245. {
  246. case 's': return Marshal.ReadInt16(adr, offset); // short
  247. case 'c': return Marshal.ReadByte(adr, offset); // char
  248. default: // double, int, int64
  249. if (type.Contains("6"))
  250. return Marshal.ReadInt64(adr, offset);
  251. else return Marshal.ReadInt32(adr, offset);
  252. }
  253. }
  254. /// <summary>
  255. /// Stores a number in binary format at the specified address in memory.
  256. /// </summary>
  257. /// <param name="number">The number to store.</param>
  258. /// <param name="address">The address in memory.</param>
  259. /// <param name="offset">The offset from <paramref name="address"/>.</param>
  260. /// <param name="type">Any type outlined in <see cref="DllCall"/>.</param>
  261. /// <returns>The address of the first byte written.</returns>
  262. public static long NumPut(int number, long address, int offset = 0, string type = "UInt")
  263. {
  264. var adr = new IntPtr(address);
  265. char mode = type.Length > 1 ? type.ToLowerInvariant()[1] : '\0';
  266. switch (mode)
  267. {
  268. case 's': Marshal.WriteInt16(adr, offset, (char)number); break; // short
  269. case 'c': Marshal.WriteByte(adr, offset, (byte)number); break; // char
  270. default: // double, int, int64
  271. if (type.Contains("6"))
  272. Marshal.WriteInt64(adr, offset, number);
  273. else Marshal.WriteInt32(adr, offset, number);
  274. break;
  275. }
  276. return adr.ToInt64() + offset;
  277. }
  278. /// <summary>
  279. /// Converts a local function to a native function pointer.
  280. /// </summary>
  281. /// <param name="function">The name of the function.</param>
  282. /// <param name="args">Unused legacy parameters.</param>
  283. /// <returns>An integer address to the function callable by unmanaged code.</returns>
  284. public static long RegisterCallback(string function, object[] args)
  285. {
  286. var method = FindLocalMethod(function);
  287. if (method == null)
  288. return 0;
  289. try
  290. {
  291. var dlg = Delegate.CreateDelegate(typeof(GenericFunction), method);
  292. return Marshal.GetFunctionPointerForDelegate(dlg).ToInt64();
  293. }
  294. catch (Exception)
  295. {
  296. return 0;
  297. }
  298. }
  299. /// <summary>
  300. /// Enlarges a variable's holding capacity. Usually only necessary for <see cref="DllCall"/>.
  301. /// </summary>
  302. /// <param name="variable">The variable to change.</param>
  303. /// <param name="capacity">Specify zero to return the current capacity.
  304. /// Otherwise <paramref name="variable"/> will be recreated as a byte array with this total length.</param>
  305. /// <param name="pad">Specify a value between 0 and 255 to initalise each index with this number.</param>
  306. /// <returns>The total capacity of <paramref name="variable"/>.</returns>
  307. public static int VarSetCapacity(ref object variable, int capacity = 0, int pad = -1)
  308. {
  309. if (capacity == 0)
  310. return Marshal.SizeOf(variable);
  311. var bytes = new byte[capacity];
  312. var fill = (byte)pad;
  313. if (pad > -1 && pad < 256)
  314. for (int i = 0; i < bytes.Length; i++)
  315. bytes[i] = fill;
  316. variable = bytes;
  317. return bytes.Length;
  318. }
  319. }
  320. }