-
-
Notifications
You must be signed in to change notification settings - Fork 77
/
IpcServiceClient.cs
128 lines (111 loc) · 4.25 KB
/
IpcServiceClient.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
using Castle.DynamicProxy;
using JKang.IpcServiceFramework.IO;
using JKang.IpcServiceFramework.Services;
using System;
using System.IO.Pipes;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace JKang.IpcServiceFramework
{
public class IpcServiceClient<TInterface>
where TInterface : class
{
private readonly string _pipeName;
private readonly IIpcMessageSerializer _serializer;
private readonly IValueConverter _converter;
public IpcServiceClient(string pipeName)
: this(pipeName, new DefaultIpcMessageSerializer(), new DefaultValueConverter())
{ }
internal IpcServiceClient(string pipeName,
IIpcMessageSerializer serializer,
IValueConverter converter)
{
_pipeName = pipeName;
_serializer = serializer;
_converter = converter;
}
public async Task InvokeAsync(Expression<Action<TInterface>> exp)
{
IpcRequest request = GetRequest(exp, new MyInterceptor());
IpcResponse response = await GetResponseAsync(request);
if (response.Succeed)
{
return;
}
else
{
throw new InvalidOperationException(response.Failure);
}
}
public async Task<TResult> InvokeAsync<TResult>(Expression<Func<TInterface, TResult>> exp)
{
IpcRequest request = GetRequest(exp, new MyInterceptor<TResult>());
IpcResponse response = await GetResponseAsync(request);
if (response.Succeed)
{
if (_converter.TryConvert(response.Data, typeof(TResult), out object @return))
{
return (TResult)@return;
}
else
{
throw new InvalidOperationException($"Unable to convert returned value to '{typeof(TResult).Name}'.");
}
}
else
{
throw new InvalidOperationException(response.Failure);
}
}
private static IpcRequest GetRequest(Expression exp, MyInterceptor interceptor)
{
if (!(exp is LambdaExpression lamdaExp))
{
throw new ArgumentException("Only support lamda expresion, ex: x => x.GetData(a, b)");
}
if (!(lamdaExp.Body is MethodCallExpression methodCallExp))
{
throw new ArgumentException("Only support calling method, ex: x => x.GetData(a, b)");
}
var proxyGenerator = new ProxyGenerator();
TInterface proxy = proxyGenerator.CreateInterfaceProxyWithoutTarget<TInterface>(interceptor);
Delegate @delegate = lamdaExp.Compile();
@delegate.DynamicInvoke(proxy);
return new IpcRequest
{
InterfaceName = typeof(TInterface).AssemblyQualifiedName,
MethodName = interceptor.LastInvocation.Method.Name,
Parameters = interceptor.LastInvocation.Arguments,
};
}
private async Task<IpcResponse> GetResponseAsync(IpcRequest request)
{
using (var client = new NamedPipeClientStream(".", _pipeName, PipeDirection.InOut, PipeOptions.None))
using (var writer = new IpcWriter(client, _serializer, leaveOpen: true))
using (var reader = new IpcReader(client, _serializer, leaveOpen: true))
{
await client.ConnectAsync();
// send request
writer.Write(request);
// receive response
return reader.ReadIpcResponse();
}
}
private class MyInterceptor : IInterceptor
{
public IInvocation LastInvocation { get; private set; }
public virtual void Intercept(IInvocation invocation)
{
LastInvocation = invocation;
}
}
private class MyInterceptor<TResult> : MyInterceptor
{
public override void Intercept(IInvocation invocation)
{
base.Intercept(invocation);
invocation.ReturnValue = default(TResult);
}
}
}
}