Skip to content

Commit

Permalink
New DLR features for Exported-Variables /part of #7
Browse files Browse the repository at this point in the history
```
l.ExVar.DLR.ADDR_SPEC
l.ExVar.DLR.ADDR_SPEC<UInt32>()
l.ExVar.get("ADDR_SPEC")
l.ExVar.get<UInt32>("ADDR_SPEC")
```

also:

* Native.Core.Field:

    * +`user` - User object for any purpose. In general, we can store any additional information that's related for specific field.
    * +`IsValidForDLR` - Checks the correct name for DLR features.
  • Loading branch information
3F committed Dec 26, 2016
1 parent f778df8 commit ff8368a
Show file tree
Hide file tree
Showing 9 changed files with 282 additions and 7 deletions.
124 changes: 122 additions & 2 deletions Conari/Core/ExVar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,30 @@
*/

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using net.r_eg.Conari.Extension;
using net.r_eg.Conari.Native;

namespace net.r_eg.Conari.Core
{
public class ExVar: IExVar
using DefaultType = Int32;

public class ExVar: DynamicObject, IExVar
{
private IProvider provider;

/// <summary>
/// Access to dynamic features like getting exported-variables at runtime.
/// </summary>
public dynamic DLR
{
get {
return this;
}
}

/// <summary>
/// Gets value from exported Variable. Full name is required.
/// </summary>
Expand All @@ -42,6 +58,17 @@ public T getVar<T>(string lpProcName)
return getField<T>(lpProcName).value;
}

/// <summary>
/// Alias to `getVar&lt;T&gt;(string lpProcName)`
/// Gets value from exported Variable. Full name is required.
/// </summary>
/// <param name="lpProcName">The full name of exported variable.</param>
/// <returns>The value from exported variable.</returns>
public DefaultType getVar(string lpProcName)
{
return getVar<DefaultType>(lpProcName);
}

/// <summary>
/// Gets value from exported Variable.
/// The main prefix will affects on this result.
Expand All @@ -54,6 +81,19 @@ public T get<T>(string variable)
return getVar<T>(provider.procName(variable));
}

/// <summary>
/// Alias to `get&lt;T&gt;(string variable)`
///
/// Gets value from exported Variable.
/// The main prefix will affects on this result.
/// </summary>
/// <param name="variable">The name of exported variable.</param>
/// <returns>The value from exported variable.</returns>
public DefaultType get(string variable)
{
return get<DefaultType>(variable);
}

/// <summary>
/// Get field with native data from export table.
/// Uses type for information about data.
Expand Down Expand Up @@ -105,6 +145,69 @@ public Native.Core.Field getField(int size, string name)
);
}

/// <summary>
/// Magic properties. Get.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
result = getFieldDLR(typeof(DefaultType), binder.Name);
return true;
}

/// <summary>
/// Magic properties. Set.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
throw new NotImplementedException("Not yet implemented.");
}

/// <summary>
/// Magic methods. Invoking.
/// </summary>
/// <![CDATA[
/// `name<return_type>()`
/// ]]>
/// <param name="binder"></param>
/// <param name="args"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
if(args?.Length > 0) {
throw new ArgumentException("Arguments are not allowed for this method.");
}
Type[] generic = getGenericArgTypes(binder).ToArray();

if(generic.Length > 1) {
throw new ArgumentException("No more than one type (as a return type) allowed for this method.");
}

result = getFieldDLR(
(generic.Length < 1) ? typeof(DefaultType) : generic[0],
binder.Name
);
return true;
}

/// <summary>
/// List of magic properties.
/// </summary>
/// <returns></returns>
public override IEnumerable<string> GetDynamicMemberNames()
{
// TODO: the exported variables are free from decorations but '@' still may be used if it's not a C linkage
// For example: LIBAPI_CPP const char* eVariableTest
// -> ?eVariableTest@API@UnLib@Conari@r_eg@net@@3PBDB
return ((ILoader)provider).PE.ExportedProcNames/*.Where(p => p.IndexOfAny(new[] { '@' }) == -1)*/;
}

public ExVar(IProvider p)
{
if(p == null) {
Expand All @@ -113,9 +216,26 @@ public ExVar(IProvider p)
provider = p;
}

protected dynamic getFieldDLR(Type type, string variable)
{
return getField(type, provider.procName(variable)).value;
}

protected Native.Core.Field getField(string name, NativeData n)
{
return n.Raw.Type.getFieldByName(name);
var ret = n.Raw.Type.getFieldByName(name);
if(ret == null) {
throw new KeyNotFoundException($"Field('{name}') was not found inside Raw.Type.");
}
return ret;
}

private IEnumerable<Type> getGenericArgTypes(InvokeMemberBinder binder)
{
// FIXME: avoid access to private members
return binder
.GetPropertyValue("Microsoft.CSharp.RuntimeBinder.ICSharpInvokeOrInvokeMemberBinder.TypeArguments", true)
as IEnumerable<Type>;
}
}
}
25 changes: 25 additions & 0 deletions Conari/Core/IExVar.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,15 @@

namespace net.r_eg.Conari.Core
{
using DefaultType = Int32;

public interface IExVar
{
/// <summary>
/// Provides dynamic features like getting exported-variables at runtime.
/// </summary>
dynamic DLR { get; }

/// <summary>
/// Gets value from exported Variable. Full name is required.
/// </summary>
Expand All @@ -36,6 +43,14 @@ public interface IExVar
/// <returns>The value from exported variable.</returns>
T getVar<T>(string lpProcName);

/// <summary>
/// Alias to `getVar&lt;T&gt;(string lpProcName)`
/// Gets value from exported Variable. Full name is required.
/// </summary>
/// <param name="lpProcName">The full name of exported variable.</param>
/// <returns>The value from exported variable.</returns>
DefaultType getVar(string lpProcName);

/// <summary>
/// Gets value from exported Variable.
/// The main prefix will affects on this result.
Expand All @@ -45,6 +60,16 @@ public interface IExVar
/// <returns>The value from exported variable.</returns>
T get<T>(string variable);

/// <summary>
/// Alias to `get&lt;T&gt;(string variable)`
///
/// Gets value from exported Variable.
/// The main prefix will affects on this result.
/// </summary>
/// <param name="variable">The name of exported variable.</param>
/// <returns>The value from exported variable.</returns>
DefaultType get(string variable);

/// <summary>
/// Get field with native data from export table.
/// Uses type for information about data.
Expand Down
10 changes: 10 additions & 0 deletions Conari/Native/Core/BType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public class BType: DynamicObject

protected byte[] data;

/// <summary>
/// Access to dynamic features like getting of values at runtime from generated fields etc.
/// </summary>
public dynamic DLR
{
get {
return this;
}
}

public TFields Fields
{
get;
Expand Down
20 changes: 20 additions & 0 deletions Conari/Native/Core/Field.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

using System;
using System.Text.RegularExpressions;

namespace net.r_eg.Conari.Native.Core
{
Expand All @@ -45,6 +46,25 @@ public sealed class Field
/// </summary>
public dynamic value;

/// <summary>
/// User object for any purpose.
/// </summary>
public object user;

/// <summary>
/// Checks the correct name for DLR features.
/// </summary>
public bool IsValidForDLR
{
get
{
if(String.IsNullOrWhiteSpace(name)) {
return false;
}
return !Regex.Match(name, "^[a-zA-Z_][a-zA-Z_0-9]*$").Success;
}
}

/// <summary>
/// Get bytes from current field.
/// </summary>
Expand Down
14 changes: 9 additions & 5 deletions Conari/Native/NativeData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using net.r_eg.Conari.Native.Core;

namespace net.r_eg.Conari.Native
Expand Down Expand Up @@ -311,10 +310,15 @@ protected virtual string fieldName(string name)
return null;
}

string rule = "^[a-zA-Z_][a-zA-Z_0-9]*$";
if(!Regex.Match(name, rule).Success) {
throw new ArgumentException($"The name `{name}` is not valid as field name. Rule: {rule}");
}
// TODO: now all fields contains IsValidForDLR property, and probably this is more flexible way...
// If use flags ('Sys | Core | Spec' or something else), we need to extract context that is more overhead.
// thus, currently we'll leave it as is.

//string rule = "^[a-zA-Z_][a-zA-Z_0-9]*$";
//if(!Regex.Match(name, rule).Success) {
// throw new ArgumentException($"The name `{name}` is not valid as field name. Rule: {rule}");
//}

return name;
}

Expand Down
1 change: 1 addition & 0 deletions ConariTest/ConariTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="ExVarTest.cs" />
<Compile Include="BindingTest.cs" />
<Compile Include="Native\Core\RawTest.cs" />
<Compile Include="Native\Core\BReaderTest.cs" />
Expand Down
82 changes: 82 additions & 0 deletions ConariTest/ExVarTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System;
using System.Runtime.InteropServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using net.r_eg.Conari;
using net.r_eg.Conari.Core;
using net.r_eg.Conari.Exceptions;
using net.r_eg.Conari.Native;
using net.r_eg.Conari.Native.Core;
using net.r_eg.Conari.Types;

namespace net.r_eg.ConariTest
{
[TestClass]
public class ExVarTest
{
private const string UNLIB_DLL = "UnLib.dll";

[TestMethod]
public void exvarTest1()
{
using(var l = new ConariL(UNLIB_DLL))
{
UInt32 expected = 0x00001CE8;

Assert.AreEqual(expected, (UInt32)l.ExVar.DLR.ADDR_SPEC, "1");
Assert.AreEqual(expected, l.ExVar.DLR.ADDR_SPEC<UInt32>(), "2");
Assert.AreEqual(expected, (UInt32)l.ExVar.get("ADDR_SPEC"), "3");
Assert.AreEqual(expected, l.ExVar.get<UInt32>("ADDR_SPEC"), "4");
Assert.AreEqual(expected, (UInt32)l.ExVar.getVar("ADDR_SPEC"), "5");
Assert.AreEqual(expected, l.ExVar.getVar<UInt32>("ADDR_SPEC"), "6");

Assert.AreEqual(expected, l.ExVar.getField<UInt32>("ADDR_SPEC").value, "7");
Assert.AreEqual(expected, l.ExVar.getField(typeof(UInt32), "ADDR_SPEC").value, "8");

byte[] raw = l.ExVar.getField(typeof(UInt32).NativeSize(), "ADDR_SPEC").value;
Assert.AreEqual(expected, BitConverter.ToUInt32(raw, 0), "9");
}
}

[TestMethod]
public void exvarTest2()
{
using(var l = new ConariL(UNLIB_DLL, "apiprefix_"))
{
bool expected = false;

Assert.AreEqual(expected, l.ExVar.DLR.GFlag<bool>(), "1");
Assert.AreEqual(expected, l.ExVar.get<bool>("GFlag"), "2");
Assert.AreEqual(expected, l.ExVar.getVar<bool>("apiprefix_GFlag"), "3");

Assert.AreEqual(expected, l.ExVar.getField<bool>("apiprefix_GFlag").value, "4");
Assert.AreEqual(expected, l.ExVar.getField(typeof(bool), "apiprefix_GFlag").value, "5");

byte[] raw = l.ExVar.getField(typeof(bool).NativeSize(), "apiprefix_GFlag").value;
Assert.AreEqual(expected, BitConverter.ToBoolean(raw, 0), "6");
}
}

[TestMethod]
public void exvarTest3()
{
using(var l = new ConariL(UNLIB_DLL))
{
string expected = "Hello World!";
string fld = "?eVariableTest@API@UnLib@Conari@r_eg@net@@3PBDB";

Assert.AreEqual(expected, (CharPtr)l.ExVar.get(fld), "1");
Assert.AreEqual(expected, (CharPtr)l.ExVar.get<IntPtr>(fld), "2");
Assert.AreEqual(expected, (CharPtr)l.ExVar.getVar(fld), "3");
Assert.AreEqual(expected, (CharPtr)l.ExVar.getVar<IntPtr>(fld), "4");

Assert.AreEqual(expected, (CharPtr)l.ExVar.getField<IntPtr>(fld).value, "5");
Assert.AreEqual(expected, (CharPtr)l.ExVar.getField(typeof(IntPtr), fld).value, "6");

byte[] raw = l.ExVar.getField(typeof(IntPtr).NativeSize(), fld).value;
CharPtr rawstr = (IntPtr.Size == sizeof(Int64)) ? (CharPtr)BitConverter.ToInt64(raw, 0) : (CharPtr)BitConverter.ToInt32(raw, 0);
Assert.AreEqual(expected, rawstr, "7");
}
}

}
}
1 change: 1 addition & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ using(var l = new ConariL("Library.dll", CallingConvention.StdCall))

```csharp
// v1.3+
l.ExVar.DLR.ADDR_SPEC // 0x00001CE8
l.ExVar.get<UInt32>("ADDR_SPEC"); // 0x00001CE8
l.ExVar.getField(typeof(UInt32).NativeSize(), "ADDR_SPEC"); // Native.Core.Field via raw size
l.Svc.native("lpProcName"); // Raw access via NativeData & Native.Core !
Expand Down
Loading

0 comments on commit ff8368a

Please sign in to comment.