├── Project ├── Codeer.Friendly │ ├── Properties │ │ ├── Resources.ja.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.ja.resx │ │ └── Resources.resx │ ├── Codeer.Friendly.snk │ ├── IDefinition.cs │ ├── IAppVarOwner.cs │ ├── Inside │ │ ├── Protocol │ │ │ ├── VarAddress.cs │ │ │ ├── IFriendlyConnector.cs │ │ │ ├── ReturnInfo.cs │ │ │ ├── ProtocolType.cs │ │ │ ├── ProtocolInfo.cs │ │ │ └── ExceptionInfo.cs │ │ ├── FriendlyInfoAttribute.cs │ │ ├── AsyncFriendlyOperationOwner.cs │ │ ├── OperationTalker.cs │ │ ├── FriendlyOperationOwner.cs │ │ ├── InformationException.cs │ │ ├── InternalException.cs │ │ ├── StaticOperationTalker.cs │ │ └── FriendlyTalker.cs │ ├── IAppVarSelf.cs │ ├── FriendlyOperation.cs │ ├── ExplicitAppVar.cs │ ├── AppVarHelper.cs │ ├── Enumerate.cs │ ├── OperationTypeInfo.cs │ ├── NewInfo.cs │ ├── FriendlyOperationException.cs │ ├── Codeer.Friendly.sln │ ├── Async.cs │ ├── Codeer.Friendly.csproj │ └── AppVar.cs ├── Codeer.Friendly.Dynamic │ ├── Properties │ │ ├── Resources.ja.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ ├── Resources.ja.resx │ │ └── Resources.resx │ ├── Codeer.Friendly.Dynamic.snk │ ├── AppVarExtensions.cs │ ├── Inside │ │ ├── TypeChahe.cs │ │ └── DynamicFriendlyOperationUtility.cs │ ├── Codeer.Friendly.Dynamic.csproj │ ├── AppFriendExtensions.cs │ ├── DynamicAppType.cs │ └── DynamicAppVar.cs └── Nuget │ ├── NuGet.exe │ ├── MakeNupack.bat │ └── Friendly.nuspec ├── Img ├── AppVar.jpg ├── Dlls.png ├── CpuType.png ├── Drivers.png ├── Libraries.jpg ├── Serialize.jpg ├── CantSerialize.jpg ├── NumericUpDwon.png ├── DriverAndScenario.jpg ├── WindowDriverTarget.png ├── DriverAndScenario.jp.jpg └── UserControlDriverTarget.png ├── FriendlyNuget128.png ├── .gitignore ├── CustomSerializer.jp.md ├── CustomSerializer.md ├── TestAutomationDesign.jp.md ├── LICENSE └── TestAutomationDesign.md /Project/Codeer.Friendly/Properties/Resources.ja.Designer.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Properties/Resources.ja.Designer.cs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Img/AppVar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/AppVar.jpg -------------------------------------------------------------------------------- /Img/Dlls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/Dlls.png -------------------------------------------------------------------------------- /Img/CpuType.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/CpuType.png -------------------------------------------------------------------------------- /Img/Drivers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/Drivers.png -------------------------------------------------------------------------------- /Img/Libraries.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/Libraries.jpg -------------------------------------------------------------------------------- /Img/Serialize.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/Serialize.jpg -------------------------------------------------------------------------------- /FriendlyNuget128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/FriendlyNuget128.png -------------------------------------------------------------------------------- /Img/CantSerialize.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/CantSerialize.jpg -------------------------------------------------------------------------------- /Img/NumericUpDwon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/NumericUpDwon.png -------------------------------------------------------------------------------- /Project/Nuget/NuGet.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Project/Nuget/NuGet.exe -------------------------------------------------------------------------------- /Img/DriverAndScenario.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/DriverAndScenario.jpg -------------------------------------------------------------------------------- /Img/WindowDriverTarget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/WindowDriverTarget.png -------------------------------------------------------------------------------- /Img/DriverAndScenario.jp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/DriverAndScenario.jp.jpg -------------------------------------------------------------------------------- /Img/UserControlDriverTarget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Img/UserControlDriverTarget.png -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Codeer.Friendly.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Project/Codeer.Friendly/Codeer.Friendly.snk -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Codeer.Friendly.Dynamic.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Codeer-Software/Friendly/HEAD/Project/Codeer.Friendly.Dynamic/Codeer.Friendly.Dynamic.snk -------------------------------------------------------------------------------- /Project/Codeer.Friendly/IDefinition.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly 2 | { 3 | #if ENG 4 | /// 5 | /// 定義であることの識別子。 6 | /// ライブラリ内で使います。 7 | /// 8 | #else 9 | /// 10 | /// 定義であることの識別子。 11 | /// ライブラリ内で使います。 12 | /// 13 | #endif 14 | public interface IDefinition { } 15 | } 16 | -------------------------------------------------------------------------------- /Project/Nuget/MakeNupack.bat: -------------------------------------------------------------------------------- 1 | rd /s /q "../ReleaseBinary" 2 | "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe" "../Codeer.Friendly/Codeer.Friendly.sln" /rebuild Release 3 | "C:\Program Files\Microsoft Visual Studio\2022\Professional\Common7\IDE\devenv.exe" "../Codeer.Friendly/Codeer.Friendly.sln" /rebuild Release-Eng 4 | nuget pack friendly.nuspec -------------------------------------------------------------------------------- /Project/Codeer.Friendly/IAppVarOwner.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly 2 | { 3 | #if ENG 4 | /// 5 | /// Demonstrating interface keeping the AppVar therein. 6 | /// 7 | #else 8 | /// 9 | /// 内部にAppVarを保持することを明示するインターフェイス。 10 | /// 11 | #endif 12 | public interface IAppVarOwner 13 | { 14 | #if ENG 15 | /// 16 | /// Variable in the application. 17 | /// 18 | #else 19 | /// 20 | /// 内部的に保持する対象アプリケーション内変数。 21 | /// 22 | #endif 23 | AppVar AppVar { get; } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/VarAddress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Codeer.Friendly.Inside.Protocol 4 | { 5 | /// 6 | /// 変数アドレス。 7 | /// 8 | [Serializable] 9 | public class VarAddress 10 | { 11 | /// 12 | /// コア。 13 | /// 14 | public int Core { get; set; } 15 | 16 | /// 17 | /// コンストラクタ 18 | /// 19 | public VarAddress() { } 20 | 21 | /// 22 | /// コンストラクタ。 23 | /// 24 | /// コア。 25 | public VarAddress(int core) 26 | { 27 | Core = core; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/IFriendlyConnector.cs: -------------------------------------------------------------------------------- 1 | using Codeer.Friendly; 2 | 3 | namespace Codeer.Friendly.Inside.Protocol 4 | { 5 | /// 6 | /// 接続者。 7 | /// 8 | public interface IFriendlyConnector 9 | { 10 | /// 11 | /// 接続者を区別するためのユニークなオブジェクト。 12 | /// 13 | object Identity { get; } 14 | 15 | /// 16 | /// 送受信。 17 | /// 18 | /// 通信情報。 19 | /// 戻り値。 20 | ReturnInfo SendAndReceive(ProtocolInfo info); 21 | 22 | /// 23 | /// アプリケーション操作クラスを取得します。 24 | /// 25 | AppFriend App { get; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/FriendlyInfoAttribute.cs: -------------------------------------------------------------------------------- 1 | #define CODE_ANALYSIS 2 | using System; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Codeer.Friendly.Inside 6 | { 7 | /// 8 | /// Friendly情報。 9 | /// 10 | [AttributeUsage(AttributeTargets.All)] 11 | [SuppressMessage("Microsoft.Performance", "CA1813:AvoidUnsealedAttributes")] 12 | public class FriendlyInfoAttribute : Attribute 13 | { 14 | private string _info; 15 | 16 | /// 17 | /// 情報。 18 | /// 19 | public string Info { get { return _info; } } 20 | 21 | /// 22 | /// コンストラクタ。 23 | /// 24 | /// 情報。 25 | public FriendlyInfoAttribute(string info) { _info = info; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/IAppVarSelf.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | namespace Codeer.Friendly 3 | { 4 | #if ENG 5 | /// 6 | /// Show that it is equivalent to him. 7 | /// Generally, please use IAppVarOwner. 8 | /// 9 | #else 10 | /// 11 | /// AppVarとほぼ等価な存在を表すインターフェイス。 12 | /// ライブラリ内で使います。 13 | /// 一般的にはIAppVarOwnerを使ってください。 14 | /// 15 | #endif 16 | [Obsolete("Please use one of the following. IAppVarOwner", false)] 17 | public interface IAppVarSelf 18 | { 19 | #if ENG 20 | /// 21 | /// Variable in the application 22 | /// 23 | #else 24 | /// 25 | /// 内部的に保持する対象アプリケーション内変数。 26 | /// 27 | #endif 28 | AppVar CodeerFriendlyAppVar { get; } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/FriendlyOperation.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly 2 | { 3 | #if ENG 4 | /// 5 | /// A delegate to call operations within the target application. 6 | /// 7 | /// Parameters corresponding to the operation. Can be null, a serializable objecr, or an AppVar. 8 | /// 9 | /// When the operation has a return value, the result is stored in a variable declared within the target class, and an object for manipulating that variable is returned. 10 | /// When there is no return value, returns null. 11 | /// 12 | #else 13 | /// 14 | /// アプリケーションに対する操作実行デリゲート。 15 | /// 16 | /// 引数。 17 | /// 戻り値。 18 | #endif 19 | public delegate AppVar FriendlyOperation(params object[] args); 20 | } 21 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/ReturnInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Codeer.Friendly.Inside.Protocol 4 | { 5 | /// 6 | /// 戻り値情報。 7 | /// 8 | [Serializable] 9 | public class ReturnInfo 10 | { 11 | /// 12 | /// 戻り値。 13 | /// 14 | public object ReturnValue { get; set; } 15 | 16 | /// 17 | /// 例外。 18 | /// 19 | public ExceptionInfo Exception { get; set; } 20 | 21 | /// 22 | /// コンストラクタ。 23 | /// 24 | public ReturnInfo() { } 25 | 26 | /// 27 | /// コンストラクタ。 28 | /// 29 | /// 戻り値。 30 | public ReturnInfo(object returnValue) 31 | { 32 | ReturnValue = returnValue; 33 | } 34 | 35 | /// 36 | /// コンストラクタ。 37 | /// 38 | /// 例外情報。 39 | public ReturnInfo(ExceptionInfo exception) 40 | { 41 | Exception = exception; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/ProtocolType.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly.Inside.Protocol 2 | { 3 | /// 4 | /// 通信タイプ。 5 | /// 6 | public enum ProtocolType 7 | { 8 | /// 9 | /// 変数初期化。 10 | /// 11 | VarInitialize, 12 | 13 | /// 14 | /// 変数生成。 15 | /// 16 | VarNew, 17 | 18 | /// 19 | /// 変数を捨てる。 20 | /// 21 | BinOff, 22 | 23 | /// 24 | /// 値を設定。 25 | /// 26 | GetValue, 27 | 28 | /// 29 | /// 値を取得。 30 | /// 31 | SetValue, 32 | 33 | /// 34 | /// 要素取得。 35 | /// 36 | GetElements, 37 | 38 | /// 39 | /// 操作。 40 | /// 41 | Operation, 42 | 43 | /// 44 | /// 空変数であるかチェック。 45 | /// 非同期実行中に使用されるので可能な限り高速に処理を戻すこと。 46 | /// 47 | IsEmptyVar, 48 | 49 | /// 50 | /// 非同期結果格納バッファ初期化。 51 | /// 52 | AsyncResultVarInitialize, 53 | 54 | /// 55 | /// 非同期操作。 56 | /// 57 | AsyncOperation, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/ExplicitAppVar.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly 2 | { 3 | #if ENG 4 | /// 5 | /// Explicit AppVar. 6 | /// If you use dynamic to argument, return value becomes dynamic. 7 | /// This class prevents it. 8 | /// 9 | #else 10 | /// 11 | /// アプリケーション内部の変数を明示するときに使用する 12 | /// dynamicを引数に使うとメソッドの戻り値もdynamicになるのでそれを防ぐ 13 | /// 14 | #endif 15 | public class ExplicitAppVar : IAppVarOwner 16 | { 17 | #if ENG 18 | /// 19 | /// Variable in the application. 20 | /// 21 | #else 22 | /// 23 | /// 内部的に保持する対象アプリケーション内変数。 24 | /// 25 | #endif 26 | public AppVar AppVar { get; private set; } 27 | 28 | #if ENG 29 | /// 30 | /// Constractor. 31 | /// 32 | /// Variable in the application. 33 | #else 34 | /// 35 | /// コンストラクタ 36 | /// 37 | /// アプリケーション内部の変数 38 | #endif 39 | public ExplicitAppVar(AppVar appVar) 40 | { 41 | AppVar = appVar; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 6 | // アセンブリに関連付けられている情報を変更するには、 7 | // これらの属性値を変更してください。 8 | [assembly: AssemblyTitle("Codeer.Friendly.Dynamic")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Codeer.Friendly.Dynamic")] 13 | [assembly: AssemblyCopyright("Copyright © 2013")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから 18 | // 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、 19 | // その型の ComVisible 属性を true に設定してください。 20 | [assembly: ComVisible(false)] 21 | 22 | // 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です 23 | [assembly: Guid("b51cd959-57c7-4df6-ba7b-26ea21bdc0e8")] 24 | 25 | // アセンブリのバージョン情報は、以下の 4 つの値で構成されています: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を 33 | // 既定値にすることができます: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("2.7.0.0")] 36 | [assembly: AssemblyFileVersion("2.7.0.0")] 37 | [assembly: System.CLSCompliant(true)] -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // アセンブリに関する一般情報は以下の属性セットをとおして制御されます。 6 | // アセンブリに関連付けられている情報を変更するには、 7 | // これらの属性値を変更してください。 8 | [assembly: AssemblyTitle("Codeer.Friendly")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Codeer.Friendly")] 13 | [assembly: AssemblyCopyright("Copyright © 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // ComVisible を false に設定すると、その型はこのアセンブリ内で COM コンポーネントから 18 | // 参照不可能になります。COM からこのアセンブリ内の型にアクセスする場合は、 19 | // その型の ComVisible 属性を true に設定してください。 20 | [assembly: ComVisible(false)] 21 | 22 | // 次の GUID は、このプロジェクトが COM に公開される場合の、typelib の ID です 23 | [assembly: Guid("0691e3ff-b712-4b4b-b34e-9b58ab8e505b")] 24 | 25 | // アセンブリのバージョン情報は、以下の 4 つの値で構成されています: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // すべての値を指定するか、下のように '*' を使ってビルドおよびリビジョン番号を 33 | // 既定値にすることができます: 34 | [assembly: AssemblyVersion("2.7.0.0")] 35 | [assembly: AssemblyFileVersion("2.7.0.0")] 36 | [assembly: Codeer.Friendly.Inside.FriendlyInfoAttribute("invisible")] 37 | [assembly: System.CLSCompliant(true)] -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/AsyncFriendlyOperationOwner.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly.Inside 2 | { 3 | /// 4 | /// 非同期操作保持クラス。 5 | /// 6 | internal class AsyncFriendlyOperationOwner 7 | { 8 | Async _async; 9 | string _operation; 10 | OperationTypeInfo _operationTypeInfo; 11 | 12 | /// 13 | /// コンストラクタ。 14 | /// 15 | /// 非同期実行クラス。 16 | /// 操作タイプ情報。(オーバーロードの解決等に使用します。) 17 | /// 操作名称。 18 | internal AsyncFriendlyOperationOwner(Async async, OperationTypeInfo operationTypeInfo, string operation) 19 | { 20 | _async = async; 21 | _operationTypeInfo = operationTypeInfo; 22 | _operation = operation; 23 | } 24 | 25 | /// 26 | /// 操作呼び出し。 27 | /// 28 | /// 引数。 29 | /// 戻り値。 30 | internal AppVar FriendlyOperation(params object[] args) 31 | { 32 | if (args == null) 33 | { 34 | args = new object[] { null }; 35 | } 36 | return _async.Invoke(_operationTypeInfo, _operation, args); 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/OperationTalker.cs: -------------------------------------------------------------------------------- 1 | using Codeer.Friendly.Inside.Protocol; 2 | 3 | namespace Codeer.Friendly.Inside 4 | { 5 | /// 6 | /// 操作通信 7 | /// 8 | public abstract class OperationTalker 9 | { 10 | /// 11 | /// アプリケーションとの接続者。 12 | /// 13 | internal abstract IFriendlyConnector FriendlyConnector { get; } 14 | 15 | /// 16 | /// 戻り値をAppVarで取得する通信。 17 | /// 18 | /// 通信タイプ。 19 | /// 操作タイプ情報。 20 | /// 操作名称。 21 | /// 引数。 22 | /// 変数。 23 | internal abstract AppVar SendAndReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments); 24 | 25 | /// 26 | /// 戻り値を値で取得する通信処理。 27 | /// 28 | /// 通信タイプ。 29 | /// 操作タイプ情報。 30 | /// 操作名称。 31 | /// 引数。 32 | /// 値。 33 | internal abstract object SendAndValueReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/FriendlyOperationOwner.cs: -------------------------------------------------------------------------------- 1 | using Codeer.Friendly.Inside.Protocol; 2 | 3 | namespace Codeer.Friendly.Inside 4 | { 5 | /// 6 | /// 操作保持クラス。 7 | /// 8 | class FriendlyOperationOwner 9 | { 10 | OperationTalker _operationTalker; 11 | OperationTypeInfo _operationTypeInfo; 12 | string _operation; 13 | 14 | /// 15 | /// コンストラクタ。 16 | /// 17 | /// 操作通信社 18 | /// 操作タイプ情報。 19 | /// 操作名称。 20 | internal FriendlyOperationOwner(OperationTalker talker, OperationTypeInfo operationTypeInfo, string operation) 21 | { 22 | _operationTalker = talker; 23 | _operationTypeInfo = operationTypeInfo; 24 | _operation = operation; 25 | } 26 | 27 | /// 28 | /// 操作実行。 29 | /// 30 | /// 引数。 31 | /// 戻り値。 32 | internal AppVar FriendlyOperation(params object[] arguments) 33 | { 34 | if (arguments == null) 35 | { 36 | arguments = new object[] { null }; 37 | } 38 | return _operationTalker.SendAndReceive(ProtocolType.Operation, _operationTypeInfo, _operation, arguments); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/AppVarExtensions.cs: -------------------------------------------------------------------------------- 1 | namespace Codeer.Friendly.Dynamic 2 | { 3 | #if ENG 4 | /// 5 | /// AppVar extension methods to provide dynamic functionality. 6 | /// 7 | #else 8 | /// 9 | /// AppVarを拡張します。 10 | /// 11 | #endif 12 | public static class AppVarExtensions 13 | { 14 | #if ENG 15 | /// 16 | /// Helper method to convert an AppVar to a DynamicAppVar and convert it into a dynamic type. 17 | /// 18 | /// AppVar. 19 | /// DynamicAppVar. 20 | #else 21 | /// 22 | /// AppVarをDynamicAppVarに変換し、dynamic型に入れるためのヘルパメソッドです。 23 | /// 24 | /// AppVar型変数。 25 | /// DynamicAppVar。 26 | #endif 27 | public static dynamic Dynamic(this AppVar src) 28 | { 29 | return new DynamicAppVar(src); 30 | } 31 | 32 | #if ENG 33 | /// 34 | /// Helper method to convert an AppVar to a DynamicAppVar and convert it into a dynamic type. 35 | /// 36 | /// AppVar Owner. 37 | /// DynamicAppVar. 38 | #else 39 | /// 40 | /// IAppVarOwnerClearlyをDynamicAppVarに変換し、dynamic型に入れるためのヘルパメソッドです。 41 | /// 42 | /// AppVar保持クラス。 43 | /// DynamicAppVar。 44 | #endif 45 | public static dynamic Dynamic(this IAppVarOwner src) 46 | { 47 | return new DynamicAppVar(src.AppVar); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/InformationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using System.Security.Permissions; 4 | 5 | namespace Codeer.Friendly.Inside 6 | { 7 | /// 8 | /// 情報通知用例外。 9 | /// 10 | [Serializable] 11 | public class InformationException : Exception 12 | { 13 | /// 14 | /// コンストラクタ。 15 | /// 16 | public InformationException(){} 17 | 18 | /// 19 | /// コンストラクタ。 20 | /// 21 | /// メッセージ。 22 | public InformationException(string message) : base(message) { } 23 | 24 | /// 25 | /// コンストラクタ。 26 | /// 27 | /// メッセージ。 28 | /// 内部例外。 29 | public InformationException(string message, Exception innerException) : base(message, innerException) { } 30 | 31 | /// 32 | /// コンストラクタ。 33 | /// 34 | /// シリアライズ情報。 35 | /// コンテキスト。 36 | protected InformationException(SerializationInfo info, StreamingContext context) 37 | : base(info, context) { } 38 | 39 | /// 40 | /// シリアライズ。 41 | /// 42 | /// シリアライズ情報。 43 | /// コンテキスト。 44 | [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] 45 | public override void GetObjectData(SerializationInfo info, StreamingContext context) 46 | { 47 | base.GetObjectData(info, context); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/InternalException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Codeer.Friendly.Properties; 3 | using System.Security.Permissions; 4 | using System.Runtime.Serialization; 5 | 6 | namespace Codeer.Friendly.Inside 7 | { 8 | /// 9 | /// 内部例外。 10 | /// 11 | [Serializable] 12 | public class InternalException : Exception 13 | { 14 | /// 15 | /// コンストラクタ。 16 | /// 17 | public InternalException(){} 18 | 19 | /// 20 | /// コンストラクタ。 21 | /// 22 | /// メッセージ。 23 | public InternalException(string message) : base(message) { } 24 | 25 | /// 26 | /// コンストラクタ。 27 | /// 28 | /// メッセージ。 29 | /// 内部例外。 30 | public InternalException(string message, Exception innerException) : base(message, innerException) { } 31 | 32 | /// 33 | /// コンストラクタ。 34 | /// 35 | /// シリアライズ情報。 36 | /// コンテキスト。 37 | protected InternalException(SerializationInfo info, StreamingContext context) 38 | : base(info, context) { } 39 | 40 | /// 41 | /// シリアライズ。 42 | /// 43 | /// シリアライズ情報。 44 | /// コンテキスト。 45 | [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] 46 | public override void GetObjectData(SerializationInfo info, StreamingContext context) 47 | { 48 | base.GetObjectData(info, context); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/ProtocolInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Codeer.Friendly.Inside.Protocol 4 | { 5 | /// 6 | /// 通信情報。 7 | /// 8 | [Serializable] 9 | public class ProtocolInfo 10 | { 11 | /// 12 | /// 通信タイプ。 13 | /// 14 | public ProtocolType ProtocolType { get; set; } 15 | 16 | /// 17 | /// 操作タイプ情報。 18 | /// 19 | public OperationTypeInfo OperationTypeInfo { get; set; } 20 | 21 | /// 22 | /// 変数アドレス。 23 | /// 24 | public VarAddress VarAddress { get; set; } 25 | 26 | /// 27 | /// タイプフルネーム。 28 | /// 29 | public string TypeFullName { get; set; } 30 | 31 | /// 32 | /// 操作名称。 33 | /// 34 | public string Operation { get; set; } 35 | 36 | /// 37 | /// 引数。 38 | /// 39 | public object[] Arguments { get; set; } 40 | 41 | /// 42 | /// コンストラクタ 43 | /// 44 | public ProtocolInfo() { } 45 | 46 | /// 47 | /// コンストラクタ。 48 | /// 49 | /// 通信タイプ。 50 | /// 操作タイプ情報。 51 | /// 変数アドレス。 52 | /// タイプフルネーム。 53 | /// 操作名称。 54 | /// 引数。 55 | public ProtocolInfo(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, VarAddress varAddress, string typeFullName, string operation, object[] arguments) 56 | { 57 | ProtocolType = protocolType; 58 | OperationTypeInfo = operationTypeInfo; 59 | VarAddress = varAddress; 60 | TypeFullName = typeFullName; 61 | Operation = operation; 62 | Arguments = arguments; 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Project/Nuget/Friendly.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Codeer.Friendly 5 | 2.7.0 6 | Friendly 7 | Codeer 8 | Codeer 9 | https://github.com/Codeer-Software/Friendly/blob/master/LICENSE 10 | https://github.com/Codeer-Software/Friendly 11 | https://raw.githubusercontent.com/Codeer-Software/Friendly/master/FriendlyNuget128.png 12 | false 13 | Friendly Common Interfaces. You can access the target application using the interface defined by this. 14 | Friendly Testing Windows WPF WinForms Win32 Automation 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/AppVarHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Codeer.Friendly 4 | { 5 | #if ENG 6 | /// 7 | /// Helper methos of AppVar. 8 | /// 9 | #else 10 | /// 11 | /// AppVarのヘルパメソッドです。 12 | /// 13 | #endif 14 | public static class AppVarHelper 15 | { 16 | #if ENG 17 | /// 18 | /// Determines whether the specified Object instances in target application are the same instance. 19 | /// 20 | /// The first object to compare. 21 | /// The second object to compare. 22 | /// is same. 23 | #else 24 | /// 25 | /// 対象プロセス内のインスタンスが同一であるかを判断します。 26 | /// 27 | /// 比較対象1。 28 | /// 比較対象2。 29 | /// インスタンスが同一であるか。 30 | #endif 31 | public static bool ReferenceEquals(AppVar lhs, AppVar rhs) 32 | { 33 | if (lhs == null) 34 | { 35 | throw new ArgumentNullException("lhs"); 36 | } 37 | if (rhs == null) 38 | { 39 | throw new ArgumentNullException("rhs"); 40 | } 41 | return (bool)lhs.App[typeof(object), "ReferenceEquals"](lhs, rhs).Core; 42 | } 43 | 44 | #if ENG 45 | /// 46 | /// Get that variables in the target application is null. 47 | /// 48 | /// Variable in the target application. 49 | /// is null. 50 | #else 51 | /// 52 | /// アプリケーション内変数がnullであるかを取得します。 53 | /// 54 | /// アプリケーション内変数。 55 | /// nullであるか。 56 | #endif 57 | public static bool IsNull(AppVar appVar) 58 | { 59 | if (appVar == null) 60 | { 61 | throw new ArgumentNullException("appVar"); 62 | } 63 | return appVar.IsNull; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/Protocol/ExceptionInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Codeer.Friendly.Inside.Protocol 4 | { 5 | /// 6 | /// 例外情報。 7 | /// 例外クラスは場合によっては、シリアライズ不可能なので、必要なデータのみ抜粋し、保持する。 8 | /// 9 | [Serializable] 10 | public class ExceptionInfo 11 | { 12 | /// 13 | /// 例外のタイプ文字列。 14 | /// 15 | public string ExceptionType { get; set; } 16 | 17 | /// 18 | /// 例外に関連付けられているヘルプ ファイルへのリンク。 19 | /// 20 | public string HelpLink { get; set; } 21 | 22 | /// 23 | /// 現在の例外を説明するメッセージ。 24 | /// 25 | public string Message { get; set; } 26 | 27 | /// 28 | /// エラーの原因となったアプリケーションまたはオブジェクトの名前。 29 | /// 30 | public string Source { get; set; } 31 | 32 | /// 33 | /// 現在の例外がスローされたときにコール スタック。 34 | /// 35 | public string StackTrace { get; set; } 36 | 37 | /// 38 | /// コンストラクタ。 39 | /// 40 | public ExceptionInfo() { } 41 | 42 | /// 43 | /// コンストラクタ。 44 | /// 45 | /// 例外。 46 | public ExceptionInfo(Exception exception) 47 | { 48 | if (exception == null) 49 | { 50 | return; 51 | } 52 | 53 | //アプリ内部でFriendly系の処理によって発生した想定内の例外はメッセージのみ返す。 54 | if (exception is InformationException) 55 | { 56 | Message = exception.Message; 57 | return; 58 | } 59 | 60 | //InternalErrorがあれば、それを利用する 61 | while (exception.InnerException != null) 62 | { 63 | exception = exception.InnerException; 64 | } 65 | Message = exception.Message; 66 | ExceptionType = exception.GetType().FullName; 67 | HelpLink = exception.HelpLink; 68 | Source = exception.Source; 69 | StackTrace = exception.StackTrace; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Inside/TypeChahe.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Codeer.Friendly.Dynamic.Inside 4 | { 5 | /// 6 | /// タイプキャッシュ。 7 | /// 8 | class TypeChahe 9 | { 10 | AppVar _typeFinder; 11 | internal Dictionary NameSpace { get; private set; } 12 | internal Dictionary Type { get; private set; } 13 | 14 | /// 15 | /// コンストラクタ 16 | /// 17 | /// アプリケーション操作クラス 18 | internal TypeChahe(AppFriend app) 19 | { 20 | _typeFinder = app.Dim(new NewInfo("Codeer.Friendly.DotNetExecutor.TypeFinder")); 21 | NameSpace = new Dictionary(); 22 | Type = new Dictionary(); 23 | } 24 | 25 | /// 26 | /// タイプ名称か。 27 | /// 28 | /// アプリケーション操作クラス。 29 | /// 名前。 30 | /// タイプ名称でなくてもキャッシュするか。 31 | /// タイプ名称か。 32 | internal bool IsTypeName(AppFriend app, string name, bool cacheNotType) 33 | { 34 | if (NameSpace.ContainsKey(name)) 35 | { 36 | return false; 37 | } 38 | bool isType = false; 39 | if (Type.TryGetValue(name, out isType)) 40 | { 41 | return isType; 42 | } 43 | if ((bool)app[typeof(object), "ReferenceEquals"](_typeFinder["GetType"](name), null).Core) 44 | { 45 | if (cacheNotType) 46 | { 47 | Type.Add(name, false); 48 | } 49 | return false; 50 | } 51 | Type.Add(name, true); 52 | int index = name.LastIndexOf('.'); 53 | if (index != -1) 54 | { 55 | string nameSpace = name.Substring(0, index); 56 | NameSpace.Remove(nameSpace); 57 | NameSpace.Add(nameSpace, true); 58 | } 59 | return true; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Build Folders (you can keep bin if you'd like, to store dlls and pdbs) 2 | [Bb]in/ 3 | [Oo]bj/ 4 | 5 | # mstest test results 6 | TestResults 7 | 8 | ## Ignore Visual Studio temporary files, build results, and 9 | ## files generated by popular Visual Studio add-ons. 10 | 11 | # User-specific files 12 | *.suo 13 | *.user 14 | *.sln.docstates 15 | 16 | # Build results 17 | [Dd]ebug/ 18 | [Rr]elease/ 19 | x64/ 20 | *_i.c 21 | *_p.c 22 | *.ilk 23 | *.meta 24 | *.obj 25 | *.pch 26 | *.pdb 27 | *.pgc 28 | *.pgd 29 | *.rsp 30 | *.sbr 31 | *.tlb 32 | *.tli 33 | *.tlh 34 | *.tmp 35 | *.log 36 | *.vspscc 37 | *.vssscc 38 | .builds 39 | 40 | # Visual C++ cache files 41 | ipch/ 42 | *.aps 43 | *.ncb 44 | *.opensdf 45 | *.sdf 46 | 47 | # Visual Studio profiler 48 | *.psess 49 | *.vsp 50 | *.vspx 51 | 52 | # Guidance Automation Toolkit 53 | *.gpState 54 | 55 | # ReSharper is a .NET coding add-in 56 | _ReSharper* 57 | 58 | # NCrunch 59 | *.ncrunch* 60 | .*crunch*.local.xml 61 | 62 | # Installshield output folder 63 | [Ee]xpress 64 | 65 | # DocProject is a documentation generator add-in 66 | DocProject/buildhelp/ 67 | DocProject/Help/*.HxT 68 | DocProject/Help/*.HxC 69 | DocProject/Help/*.hhc 70 | DocProject/Help/*.hhk 71 | DocProject/Help/*.hhp 72 | DocProject/Help/Html2 73 | DocProject/Help/html 74 | 75 | # Click-Once directory 76 | publish 77 | 78 | # Publish Web Output 79 | *.Publish.xml 80 | 81 | # NuGet Packages Directory 82 | packages 83 | 84 | # Windows Azure Build Output 85 | csx 86 | *.build.csdef 87 | 88 | # Windows Store app package directory 89 | AppPackages/ 90 | 91 | # Others 92 | [Bb]in 93 | [Oo]bj 94 | sql 95 | TestResults 96 | [Tt]est[Rr]esult* 97 | *.Cache 98 | ClientBin 99 | [Ss]tyle[Cc]op.* 100 | ~$* 101 | *.dbmdl 102 | Generated_Code #added for RIA/Silverlight projects 103 | 104 | # Backup & report files from converting an old project file to a newer 105 | # Visual Studio version. Backup files are not needed, because we have git ;-) 106 | _UpgradeReport_Files/ 107 | Backup*/ 108 | UpgradeLog*.XML 109 | 110 | *.nupkg 111 | ReleaseBinary/ 112 | TestResults/ 113 | .vs/ -------------------------------------------------------------------------------- /CustomSerializer.jp.md: -------------------------------------------------------------------------------- 1 | # FriendlyCustomSerializerSample 2 | Friendlyでカスタマイズされたシリアライザを利用するときのサンプルです。 3 | EnableUnsafeBinaryFormatterSerializationをfalseにしたアプリをFriedlyで操作したい場合に利用します。 4 | 背景としてFriendlyではプロセス間通信にBinaryFormatterを利用しているのでそれが使えなくなります。 5 | 6 | ## 利用方法 7 | ```csharp 8 | WindowsAppFriend.SetCustomSerializer(); 9 | ``` 10 | を呼び出すことでシリアライザを登録できます。 11 | 注意点としては登録したシリアライザは操作対象プロセスでも使われるため定義しているアセンブリをdllインジェクションします。 12 | 操作対象プロセスで動作することを前提としたアセンブリで実装してください。 13 | 14 | ```csharp 15 | public void Test() 16 | { 17 | //カスタムシリアライザの設定 18 | //最初に一回呼び出す 19 | WindowsAppFriend.SetCustomSerializer(); 20 | 21 | //WindowsAppFriendを生成 22 | var app = new WindowsAppFriend(Process.Start(info)); 23 | 24 | //通常のFriendlyの操作 25 | var formControls = app.AttachFormControls(); 26 | formControls.button.EmulateClick(); 27 | formControls.checkBox.EmulateCheck(CheckState.Checked); 28 | formControls.comboBox.EmulateChangeText("Item-3"); 29 | formControls.comboBox.EmulateChangeSelect(2); 30 | formControls.radioButton1.EmulateCheck(); 31 | formControls.radioButton1.EmulateCheck(); 32 | } 33 | ``` 34 | 35 | ## カスタムシリアライザ 36 | ICustomSerializerを実装します。 37 | 実装方法は任意ですが、本サンプルでは[MessagePack](https://www.nuget.org/packages/MessagePack)を利用しています。 38 | 39 | ```csharp 40 | public class IntPtrFormatter : IMessagePackFormatter 41 | { 42 | public void Serialize(ref MessagePackWriter writer, IntPtr value, MessagePackSerializerOptions options) 43 | => writer.Write(value.ToInt64()); 44 | 45 | public IntPtr Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 46 | => new IntPtr(reader.ReadInt64()); 47 | } 48 | 49 | public class CustomSerializer : ICustomSerializer 50 | { 51 | MessagePackSerializerOptions customOptions = MessagePackSerializerOptions 52 | .Standard 53 | .WithResolver( 54 | CompositeResolver.Create( 55 | new IMessagePackFormatter[] { new IntPtrFormatter() }, 56 | new IFormatterResolver[] { TypelessContractlessStandardResolver.Instance } 57 | ) 58 | ); 59 | 60 | public object Deserialize(byte[] bin) 61 | => MessagePackSerializer.Typeless.Deserialize(bin, customOptions); 62 | 63 | public Assembly[] GetRequiredAssemblies() => [GetType().Assembly, typeof(MessagePackSerializer).Assembly]; 64 | 65 | public byte[] Serialize(object obj) 66 | => MessagePackSerializer.Typeless.Serialize(obj, customOptions); 67 | } 68 | ``` 69 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Enumerate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections; 3 | using Codeer.Friendly.Inside.Protocol; 4 | 5 | namespace Codeer.Friendly 6 | { 7 | #if ENG 8 | /// 9 | /// When the variable in application corresponds to repetition processing, repetitive processing by foreach can be executed. (in .Net, when IEnumerator is inherited). 10 | /// 11 | #else 12 | /// 13 | /// アプリケーション内の変数が繰り返し処理に対応している場合(.NetならIEnumeratorを継承している場合)foreachによる反復処理を実行できます。 14 | /// 15 | #endif 16 | public class Enumerate : IEnumerable 17 | { 18 | AppVar _enumerable; 19 | 20 | #if ENG 21 | /// 22 | /// Constractor. 23 | /// 24 | /// An enumerable variable in the application. 25 | #else 26 | /// 27 | /// コンストラクタ。 28 | /// 29 | /// 反復処理可能なアプリケーション内変数。 30 | #endif 31 | public Enumerate(AppVar enumerable) 32 | { 33 | _enumerable = enumerable; 34 | } 35 | 36 | #if ENG 37 | /// 38 | /// Produces an enumerator for the provided variable. 39 | /// 40 | /// An enumerator that allows iterative processing on the variable. 41 | #else 42 | /// 43 | /// コレクションを反復処理する列挙子を返します。 44 | /// 45 | /// コレクションを反復処理するために使用できる列挙子。 46 | #endif 47 | public IEnumerator GetEnumerator() 48 | { 49 | VarAddress[] core = (VarAddress[])_enumerable.SendAndValueReceive(ProtocolType.GetElements, null, string.Empty, new object[0]); 50 | List elements = new List(); 51 | for (int i = 0; i < core.Length; i++) 52 | { 53 | elements.Add(new AppVar(_enumerable.FriendlyConnector, core[i])); 54 | } 55 | return elements.GetEnumerator(); 56 | } 57 | 58 | #if ENG 59 | /// 60 | /// Produces an enumerator for the provided variable. 61 | /// 62 | /// An enumerator that allows iterative processing on the variable. 63 | #else 64 | /// 65 | /// コレクションを反復処理する列挙子を返します。 66 | /// 67 | /// コレクションを反復処理するために使用できる列挙子。 68 | #endif 69 | IEnumerator IEnumerable.GetEnumerator() 70 | { 71 | return GetEnumerator(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /CustomSerializer.md: -------------------------------------------------------------------------------- 1 | # Friendly Custom Serializer Sample 2 | 3 | This sample shows how to use a customized serializer with Friendly. 4 | Use it when you want to control an app with Friendly that has `EnableUnsafeBinaryFormatterSerialization` set to `false`. 5 | 6 | **Background:** Friendly uses `BinaryFormatter` for inter process communication, so when `BinaryFormatter` cannot be used, you need to provide an alternative serializer. 7 | 8 | ## Usage 9 | 10 | ```csharp 11 | WindowsAppFriend.SetCustomSerializer(); 12 | ``` 13 | Call this to register the serializer. 14 | 15 | **Note:** The registered serializer will also be used inside the target process, so the assembly that defines it will be injected (DLL injection). Implement it as an assembly that is safe to run in the target process. 16 | 17 | ```csharp 18 | public void Test() 19 | { 20 | // Configure the custom serializer (call once at startup) 21 | WindowsAppFriend.SetCustomSerializer(); 22 | 23 | // Create WindowsAppFriend 24 | var app = new WindowsAppFriend(Process.Start(info)); 25 | 26 | // Typical Friendly operations 27 | var formControls = app.AttachFormControls(); 28 | formControls.button.EmulateClick(); 29 | formControls.checkBox.EmulateCheck(CheckState.Checked); 30 | formControls.comboBox.EmulateChangeText("Item-3"); 31 | formControls.comboBox.EmulateChangeSelect(2); 32 | formControls.radioButton1.EmulateCheck(); 33 | formControls.radioButton1.EmulateCheck(); 34 | } 35 | ``` 36 | 37 | ## Custom serializer 38 | 39 | Implement `ICustomSerializer`. 40 | Any implementation style is fine; this sample uses [MessagePack](https://www.nuget.org/packages/MessagePack). 41 | 42 | ```csharp 43 | public class IntPtrFormatter : IMessagePackFormatter 44 | { 45 | public void Serialize(ref MessagePackWriter writer, IntPtr value, MessagePackSerializerOptions options) 46 | => writer.Write(value.ToInt64()); 47 | 48 | public IntPtr Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options) 49 | => new IntPtr(reader.ReadInt64()); 50 | } 51 | 52 | public class CustomSerializer : ICustomSerializer 53 | { 54 | MessagePackSerializerOptions customOptions = MessagePackSerializerOptions 55 | .Standard 56 | .WithResolver( 57 | CompositeResolver.Create( 58 | new IMessagePackFormatter[] { new IntPtrFormatter() }, 59 | new IFormatterResolver[] { TypelessContractlessStandardResolver.Instance } 60 | ) 61 | ); 62 | 63 | public object Deserialize(byte[] bin) 64 | => MessagePackSerializer.Typeless.Deserialize(bin, customOptions); 65 | 66 | public Assembly[] GetRequiredAssemblies() => [GetType().Assembly, typeof(MessagePackSerializer).Assembly]; 67 | 68 | public byte[] Serialize(object obj) 69 | => MessagePackSerializer.Typeless.Serialize(obj, customOptions); 70 | } 71 | ``` -------------------------------------------------------------------------------- /Project/Codeer.Friendly/OperationTypeInfo.cs: -------------------------------------------------------------------------------- 1 | #define CODE_ANALYSIS 2 | using System; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Codeer.Friendly 6 | { 7 | #if ENG 8 | /// 9 | /// Used to specify parameters for a FriendlyOperation when there is a need to resolve overloads or call an operation on a parent class. 10 | /// Please refer to samples. 11 | /// 12 | #else 13 | /// 14 | /// FriendlyOperationの型を確定させるための情報です。 15 | /// オーバーロードの解決と、親クラスの操作呼び出しに使用します。 16 | /// 17 | #endif 18 | [Serializable] 19 | public class OperationTypeInfo 20 | { 21 | #if ENG 22 | /// 23 | /// Returns the full class name of the target type for the target operation. 24 | /// 25 | #else 26 | /// 27 | /// 操作を保持する型フルネームです。 28 | /// 29 | #endif 30 | public string Target { get; set; } 31 | 32 | #if ENG 33 | /// 34 | /// Returns an array of the full class names of the target operation's parameters. 35 | /// 36 | #else 37 | /// 38 | /// 引数の型のフルネーム配列。 39 | /// 40 | #endif 41 | public string[] Arguments { get; set; } 42 | 43 | #if ENG 44 | /// 45 | /// Constructor. 46 | /// 47 | #else 48 | /// 49 | /// コンストラクタ。 50 | /// 51 | #endif 52 | public OperationTypeInfo() { } 53 | 54 | #if ENG 55 | /// 56 | /// Constructor. 57 | /// 58 | /// The full class name of the target type for the target operation. 59 | /// The full class names of the the target operation's parameters. 60 | #else 61 | /// 62 | /// コンストラクタ。 63 | /// 64 | /// 操作を保持する型フルネームです。 65 | /// 引数の型のフルネーム配列。 66 | #endif 67 | [SuppressMessage("Microsoft.Globalization", "CA1303:DoNotPassLiteralsAsLocalizedParameters", MessageId = "System.ArgumentException.#ctor(System.String)")] 68 | public OperationTypeInfo(string target, params string[] arguments) 69 | { 70 | //引数チェック 71 | if (string.IsNullOrEmpty(target)) 72 | { 73 | throw new ArgumentNullException("target"); 74 | } 75 | if (arguments == null) 76 | { 77 | throw new ArgumentNullException("arguments"); 78 | } 79 | for (int i = 0; i < arguments.Length; i++) 80 | { 81 | if (string.IsNullOrEmpty(arguments[i])) 82 | { 83 | throw new ArgumentException("arguments is invalid"); 84 | } 85 | } 86 | Target = target; 87 | Arguments = arguments; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/StaticOperationTalker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Codeer.Friendly.Inside.Protocol; 3 | using Codeer.Friendly.Properties; 4 | 5 | namespace Codeer.Friendly.Inside 6 | { 7 | /// 8 | /// Static操作通信 9 | /// 10 | class StaticOperationTalker : OperationTalker 11 | { 12 | readonly IFriendlyConnector _friendlyConnector; 13 | readonly string _staticOperationTypeFullName; 14 | 15 | /// 16 | /// アプリケーションとの接続者。 17 | /// 18 | internal override IFriendlyConnector FriendlyConnector { get { return _friendlyConnector; } } 19 | 20 | /// 21 | /// コンストラクタ。 22 | /// 23 | /// 操作送信者。 24 | /// タイプフルネーム。 25 | internal StaticOperationTalker(IFriendlyConnector friendlyConnector, string typeFullName) 26 | { 27 | _friendlyConnector = friendlyConnector; 28 | _staticOperationTypeFullName = typeFullName; 29 | } 30 | 31 | /// 32 | /// 操作取得。 33 | /// 34 | /// 操作名称。 35 | /// 操作タイプ情報。 36 | /// 非同期実行オブジェクト。 37 | /// 操作 38 | internal FriendlyOperation this[string operation, OperationTypeInfo operationTypeInfo, Async async] 39 | { 40 | get 41 | { 42 | if (async == null) 43 | { 44 | return new FriendlyOperationOwner(this, operationTypeInfo, operation).FriendlyOperation; 45 | } 46 | else 47 | { 48 | async.Initialize(this); 49 | return new AsyncFriendlyOperationOwner(async, operationTypeInfo, operation).FriendlyOperation; 50 | } 51 | } 52 | } 53 | 54 | /// 55 | /// 戻り値をAppVarで取得する通信。 56 | /// 57 | /// 通信タイプ。 58 | /// 操作タイプ情報。 59 | /// 操作名称。 60 | /// 引数。 61 | /// 変数。 62 | internal override AppVar SendAndReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments) 63 | { 64 | return FriendlyTalker.SendAndVarReceive(this, _friendlyConnector, protocolType, operationTypeInfo, null, _staticOperationTypeFullName, operation, arguments); 65 | } 66 | 67 | /// 68 | /// 戻り値を値で取得する通信処理。 69 | /// 70 | /// 通信タイプ。 71 | /// 操作タイプ情報。 72 | /// 操作名称。 73 | /// 引数。 74 | /// 値。 75 | internal override object SendAndValueReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments) 76 | { 77 | return FriendlyTalker.SendAndValueReceive(this, _friendlyConnector, protocolType, operationTypeInfo, null, _staticOperationTypeFullName, operation, arguments); 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/NewInfo.cs: -------------------------------------------------------------------------------- 1 | #define CODE_ANALYSIS 2 | using System; 3 | using System.Diagnostics.CodeAnalysis; 4 | 5 | namespace Codeer.Friendly 6 | { 7 | #if ENG 8 | /// 9 | /// Used to provide information when instantiating an object in an application. 10 | /// Stores generation information and constructor parameters. 11 | /// 12 | #else 13 | /// 14 | /// アプリケーション内部でオブジェクトを生成するための情報を表すクラスです。 15 | /// 生成する型情報とコンストラクタ引数を保持します。 16 | /// 17 | #endif 18 | public class NewInfo 19 | { 20 | string _typeFullName; 21 | object[] _arguments; 22 | 23 | #if ENG 24 | /// 25 | /// Returns the type's full name. 26 | /// 27 | #else 28 | /// 29 | /// タイプフルネーム。 30 | /// 31 | #endif 32 | public string TypeFullName { get { return _typeFullName; } } 33 | 34 | #if ENG 35 | /// 36 | /// Returns the set of constructor parameters. 37 | /// 38 | #else 39 | /// 40 | /// 引数。 41 | /// 42 | #endif 43 | public object[] Arguments { get { return _arguments; } } 44 | 45 | #if ENG 46 | /// 47 | /// Constructor. 48 | /// 49 | /// Fully qualified type name of the generated object. 50 | /// 51 | /// Arguments to be passed to the object's constructor. 52 | /// Can be AppVars or serializable objects. 53 | /// 54 | #else 55 | /// 56 | /// コンストラクタ。 57 | /// 58 | /// 生成する型のタイプフルネーム。 59 | /// 生成引数。 60 | #endif 61 | public NewInfo(string typeFullName, params object[] arguments) 62 | { 63 | _typeFullName = typeFullName; 64 | _arguments = arguments; 65 | if (_arguments == null) 66 | { 67 | _arguments = new object[] { null }; 68 | } 69 | } 70 | 71 | #if ENG 72 | /// 73 | /// Constractor. 74 | /// 75 | /// The type of the object to create. 76 | /// 77 | /// Arguments to be passed to the object's constructor. 78 | /// Can be AppVars or serializable objects. 79 | /// 80 | #else 81 | /// 82 | /// コンストラクタ。 83 | /// 84 | /// タイプ。 85 | /// 生成引数。 86 | #endif 87 | [SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")] 88 | public NewInfo(Type type, params object[] args) : this(type.FullName, args) { } 89 | } 90 | 91 | #if ENG 92 | /// 93 | /// Used to provide information when instantiating an object in an application. 94 | /// Stores generation information and constructor parameters. 95 | /// 96 | #else 97 | /// 98 | /// アプリケーション内部でオブジェクトを生成するための情報を表すクラスです。 99 | /// 生成する型情報とコンストラクタ引数を保持します。 100 | /// 101 | #endif 102 | public class NewInfo : NewInfo 103 | { 104 | #if ENG 105 | /// 106 | /// Constructor for NewInfo<T>, not NewInfo. 107 | /// Provides one way of indicating the desired type. 108 | /// 109 | /// 110 | /// Arguments to be passed to the object's constructor. 111 | /// Can be AppVars or serializable objects. 112 | /// 113 | #else 114 | /// 115 | /// コンストラクタ。 116 | /// 117 | /// 生成引数。 118 | #endif 119 | public NewInfo(params object[] args) : base(typeof(T), args) { } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // このコードはツールによって生成されました。 4 | // ランタイム バージョン:4.0.30319.18052 5 | // 6 | // このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 7 | // コードが再生成されるときに損失したりします。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Codeer.Friendly.Dynamic.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 17 | /// 18 | // このクラスは StronglyTypedResourceBuilder クラスが ResGen 19 | // または Visual Studio のようなツールを使用して自動生成されました。 20 | // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に 21 | // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Codeer.Friendly.Dynamic.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、 51 | /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Multiple Async arguments were found. Only one Async argument can be specified per call. に類似しているローカライズされた文字列を検索します。 65 | /// 66 | internal static string ErrorAsyncArgument { 67 | get { 68 | return ResourceManager.GetString("ErrorAsyncArgument", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Async cannot be used when instantiating an object. に類似しているローカライズされた文字列を検索します。 74 | /// 75 | internal static string ErrorInstanceCreateCantUseAsync { 76 | get { 77 | return ResourceManager.GetString("ErrorInstanceCreateCantUseAsync", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Multiple OperationTypeInfo arguments were found. Only one OperationTypeInfo argument can be specified per call. に類似しているローカライズされた文字列を検索します。 83 | /// 84 | internal static string ErrorOperationTypeInfoArgument { 85 | get { 86 | return ResourceManager.GetString("ErrorOperationTypeInfoArgument", resourceCulture); 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Inside/DynamicFriendlyOperationUtility.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Codeer.Friendly.Dynamic.Properties; 4 | 5 | namespace Codeer.Friendly.Dynamic.Inside 6 | { 7 | /// 8 | /// DynamicでFriendlyOperationを実行するためのユーティリティー。 9 | /// 10 | static class DynamicFriendlyOperationUtility 11 | { 12 | /// 13 | /// FriendlyOperationを取得。 14 | /// 15 | /// 対象。 16 | /// 操作名称。 17 | /// 非同期実行オブジェクト。 18 | /// 操作タイプ情報。 19 | /// FriendlyOperation。 20 | internal static FriendlyOperation GetFriendlyOperation(AppVar target, string name, Async async, OperationTypeInfo typeInfo) 21 | { 22 | if (async != null && typeInfo != null) 23 | { 24 | return target[name, typeInfo, async]; 25 | } 26 | else if (async != null) 27 | { 28 | return target[name, async]; 29 | } 30 | else if (typeInfo != null) 31 | { 32 | return target[name, typeInfo]; 33 | } 34 | return target[name]; 35 | } 36 | 37 | /// 38 | /// FriendlyOperationを取得。 39 | /// 40 | /// 対象。 41 | /// 操作名称。 42 | /// 非同期実行オブジェクト。 43 | /// 操作タイプ情報。 44 | /// FriendlyOperation。 45 | internal static FriendlyOperation GetFriendlyOperation(AppFriend target, string name, Async async, OperationTypeInfo typeInfo) 46 | { 47 | if (async != null && typeInfo != null) 48 | { 49 | return target[name, typeInfo, async]; 50 | } 51 | else if (async != null) 52 | { 53 | return target[name, async]; 54 | } 55 | else if (typeInfo != null) 56 | { 57 | return target[name, typeInfo]; 58 | } 59 | return target[name]; 60 | } 61 | 62 | /// 63 | /// 引数を解決する。 64 | /// 65 | /// 元引数。 66 | /// 非同期実行オブジェクト。 67 | /// 操作タイプ情報。 68 | /// 解決した後の引数。 69 | internal static object[] ResolveArguments(object[] srcArgs, out Async async, out OperationTypeInfo typeInfo) 70 | { 71 | return ResolveAsyncAndTypeInfo(srcArgs, out async, out typeInfo); 72 | } 73 | 74 | /// 75 | /// setter時に引数に値を加える。 76 | /// 77 | /// 元引数。 78 | /// setする値。 79 | /// 加えた後の引数。 80 | internal static object[] AddSetterValue(object[] argsSrc, object value) 81 | { 82 | object[] args = new object[argsSrc.Length + 1]; 83 | Array.Copy(argsSrc, args, argsSrc.Length); 84 | args[argsSrc.Length] = value; 85 | return args; 86 | } 87 | 88 | /// 89 | /// 非同期実行オブジェクトと操作タイプ情報に関して引数を解決する。 90 | /// 91 | /// 元引数。 92 | /// 非同期実行オブジェクト。 93 | /// 操作タイプ情報。 94 | /// 解決した後の引数。 95 | private static object[] ResolveAsyncAndTypeInfo(object[] srcArgs, out Async async, out OperationTypeInfo typeInfo) 96 | { 97 | List list = new List(); 98 | async = null; 99 | typeInfo = null; 100 | foreach(object element in srcArgs) 101 | { 102 | Async checkAsync = element as Async; 103 | if (checkAsync != null) 104 | { 105 | if (async != null) 106 | { 107 | throw new FriendlyOperationException(Resources.ErrorAsyncArgument); 108 | } 109 | async = checkAsync; 110 | continue; 111 | } 112 | 113 | OperationTypeInfo checkInfo = element as OperationTypeInfo; 114 | if (checkInfo != null) 115 | { 116 | if (typeInfo != null) 117 | { 118 | throw new FriendlyOperationException(Resources.ErrorOperationTypeInfoArgument); 119 | } 120 | typeInfo = checkInfo; 121 | continue; 122 | } 123 | 124 | list.Add(element); 125 | } 126 | return list.ToArray(); 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/FriendlyOperationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Codeer.Friendly.Inside.Protocol; 3 | using Codeer.Friendly.Properties; 4 | using System.Runtime.Serialization; 5 | using System.Security.Permissions; 6 | using System.Diagnostics.CodeAnalysis; 7 | using System.Globalization; 8 | 9 | namespace Codeer.Friendly 10 | { 11 | #if ENG 12 | /// 13 | /// Inherits the Exception class 14 | /// An exception thrown while a FriendlyOperation executes within the target application. 15 | /// 16 | #else 17 | /// 18 | /// Exceptionクラスを継承します。 19 | /// テスト対象アプリケーションとの通信中、FriendlyOperation実行中に発生する例外です。 20 | /// 21 | #endif 22 | [Serializable] 23 | public class FriendlyOperationException : Exception 24 | { 25 | #if ENG 26 | /// 27 | /// Infomation. 28 | /// 29 | #else 30 | /// 31 | /// 例外情報。 32 | /// 33 | #endif 34 | public ExceptionInfo ExceptionInfo { get; set; } 35 | 36 | #if ENG 37 | /// 38 | /// Constractor. 39 | /// 40 | #else 41 | /// 42 | /// コンストラクタ。 43 | /// 44 | #endif 45 | public FriendlyOperationException(){} 46 | 47 | #if ENG 48 | /// 49 | /// Constractor. 50 | /// 51 | /// Message. 52 | #else 53 | /// 54 | /// コンストラクタ。 55 | /// 56 | /// メッセージ。 57 | #endif 58 | public FriendlyOperationException(string message) : base(message) { } 59 | 60 | #if ENG 61 | /// 62 | /// Constractor. 63 | /// 64 | /// Constractor. 65 | /// Internal Exception. 66 | #else 67 | /// 68 | /// コンストラクタ。 69 | /// 70 | /// メッセージ。 71 | /// 内部例外。 72 | #endif 73 | public FriendlyOperationException(string message, Exception innerException) : base(message, innerException) { } 74 | 75 | #if ENG 76 | /// 77 | /// Constractor.。 78 | /// 79 | /// Infomation 80 | #else 81 | /// 82 | /// コンストラクタ。 83 | /// 84 | /// 例外情報。 85 | #endif 86 | public FriendlyOperationException(ExceptionInfo info) : base(ExceptionInfoMessageFormat(info)) 87 | { 88 | ExceptionInfo = info; 89 | } 90 | 91 | #if ENG 92 | /// 93 | /// Constractor. 94 | /// 95 | /// Serialize Infomation. 96 | /// Serialize Context. 97 | #else 98 | /// 99 | /// コンストラクタ。 100 | /// 101 | /// シリアライズ情報。 102 | /// コンテキスト。 103 | #endif 104 | protected FriendlyOperationException(SerializationInfo info, StreamingContext context) 105 | : base(info, context) 106 | { 107 | if (info == null) 108 | { 109 | throw new ArgumentNullException("info"); 110 | } 111 | ExceptionInfo = (ExceptionInfo)info.GetValue("_exceptionInfo", typeof(ExceptionInfo)); 112 | } 113 | 114 | #if ENG 115 | /// 116 | /// Serialize. 117 | /// 118 | /// Serialize Infomation. 119 | /// Serialize Context. 120 | #else 121 | /// 122 | /// シリアライズ。 123 | /// 124 | /// シリアライズ情報。 125 | /// コンテキスト。 126 | #endif 127 | [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] 128 | public override void GetObjectData(SerializationInfo info, StreamingContext context) 129 | { 130 | if (info == null) 131 | { 132 | throw new ArgumentNullException("info"); 133 | } 134 | 135 | info.AddValue("_exceptionInfo", ExceptionInfo); 136 | base.GetObjectData(info, context); 137 | } 138 | 139 | /// 140 | /// 例外情報をメッセージ文字列にするフォーマット。 141 | /// 142 | /// 例外情報。 143 | /// メッセージ文字列。 144 | static string ExceptionInfoMessageFormat(ExceptionInfo info) 145 | { 146 | if (string.IsNullOrEmpty(info.StackTrace)) 147 | { 148 | return info.Message; 149 | } 150 | return string.Format(CultureInfo.CurrentCulture, Resources.ExceptionInfoFormat, 151 | info.Message, 152 | info.ExceptionType, 153 | info.Source, 154 | info.StackTrace, 155 | info.HelpLink); 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Codeer.Friendly.Dynamic.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bin\Release-Eng\ 5 | TRACE;ENG 6 | bin\Release\Codeer.Friendly.Dynamic.XML 7 | true 8 | pdbonly 9 | AnyCPU 10 | prompt 11 | 12 | 13 | 14 | Debug 15 | AnyCPU 16 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1} 17 | Library 18 | Properties 19 | Codeer.Friendly.Dynamic 20 | Codeer.Friendly.Dynamic 21 | v4.0 22 | 512 23 | 24 | 25 | 26 | true 27 | full 28 | false 29 | bin\Debug\ 30 | DEBUG;TRACE 31 | prompt 32 | 4 33 | bin\Debug\Codeer.Friendly.Dynamic.XML 34 | 35 | 36 | pdbonly 37 | true 38 | bin\Release\ 39 | TRACE 40 | prompt 41 | 4 42 | bin\Release\Codeer.Friendly.Dynamic.XML 43 | 44 | 45 | true 46 | 47 | 48 | Codeer.Friendly.Dynamic.snk 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | True 64 | True 65 | Resources.resx 66 | 67 | 68 | Resources.ja.resx 69 | True 70 | True 71 | 72 | 73 | 74 | 75 | ResXFileCodeGenerator 76 | Resources.Designer.cs 77 | 78 | 79 | ResXFileCodeGenerator 80 | Resources.ja.Designer.cs 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D} 89 | Codeer.Friendly 90 | 91 | 92 | 93 | 94 | if $(ConfigurationName) == Release Copy "$(TargetPath)" "../../../ReleaseBinary/$(TargetFileName)" 95 | if $(ConfigurationName) == Release-Eng Copy "$(TargetDir)\$(TargetName).xml" "../../../ReleaseBinary/$(TargetName).xml" 96 | if $(ConfigurationName) == Release Copy "$(TargetDir)\$(TargetName).xml" "../../../ReleaseBinary/ja/$(TargetName).xml" 97 | if $(ConfigurationName) == Release Copy "$(TargetDir)\ja\$(TargetName).resources.dll" "../../../ReleaseBinary/ja/$(TargetName).resources.dll" 98 | 99 | 106 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Codeer.Friendly.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29102.190 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeer.Friendly", "Codeer.Friendly.csproj", "{8CD64A7C-848F-4466-B5F4-8C4842A6316D}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codeer.Friendly.Dynamic", "..\Codeer.Friendly.Dynamic\Codeer.Friendly.Dynamic.csproj", "{04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nuget", "Nuget", "{76D46E6D-9E52-457C-A220-69322EFCC6B5}" 11 | ProjectSection(SolutionItems) = preProject 12 | ..\Nuget\Friendly.nuspec = ..\Nuget\Friendly.nuspec 13 | EndProjectSection 14 | EndProject 15 | Global 16 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 17 | Debug|Any CPU = Debug|Any CPU 18 | Debug|Mixed Platforms = Debug|Mixed Platforms 19 | Debug|Win32 = Debug|Win32 20 | Debug|x64 = Debug|x64 21 | Debug|x86 = Debug|x86 22 | Release|Any CPU = Release|Any CPU 23 | Release|Mixed Platforms = Release|Mixed Platforms 24 | Release|Win32 = Release|Win32 25 | Release|x64 = Release|x64 26 | Release|x86 = Release|x86 27 | Release-Eng|Any CPU = Release-Eng|Any CPU 28 | Release-Eng|Mixed Platforms = Release-Eng|Mixed Platforms 29 | Release-Eng|Win32 = Release-Eng|Win32 30 | Release-Eng|x64 = Release-Eng|x64 31 | Release-Eng|x86 = Release-Eng|x86 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 37 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 38 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|Win32.ActiveCfg = Debug|Any CPU 39 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|x64.ActiveCfg = Debug|Any CPU 40 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Debug|x86.ActiveCfg = Debug|Any CPU 41 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|Any CPU.ActiveCfg = Release|Any CPU 42 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|Any CPU.Build.0 = Release|Any CPU 43 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 44 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|Mixed Platforms.Build.0 = Release|Any CPU 45 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|Win32.ActiveCfg = Release|Any CPU 46 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|x64.ActiveCfg = Release|Any CPU 47 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release|x86.ActiveCfg = Release|Any CPU 48 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|Any CPU.ActiveCfg = Release-Eng|Any CPU 49 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|Any CPU.Build.0 = Release-Eng|Any CPU 50 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|Mixed Platforms.ActiveCfg = Release-Eng|Any CPU 51 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|Mixed Platforms.Build.0 = Release-Eng|Any CPU 52 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|Win32.ActiveCfg = Release-Eng|Any CPU 53 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|x64.ActiveCfg = Release-Eng|Any CPU 54 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D}.Release-Eng|x86.ActiveCfg = Release-Eng|Any CPU 55 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 56 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 57 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU 58 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU 59 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|Win32.ActiveCfg = Debug|Any CPU 60 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|x64.ActiveCfg = Debug|Any CPU 61 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Debug|x86.ActiveCfg = Debug|Any CPU 62 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|Any CPU.ActiveCfg = Release|Any CPU 63 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|Any CPU.Build.0 = Release|Any CPU 64 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU 65 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|Mixed Platforms.Build.0 = Release|Any CPU 66 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|Win32.ActiveCfg = Release|Any CPU 67 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|x64.ActiveCfg = Release|Any CPU 68 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release|x86.ActiveCfg = Release|Any CPU 69 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|Any CPU.ActiveCfg = Release-Eng|Any CPU 70 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|Any CPU.Build.0 = Release-Eng|Any CPU 71 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|Mixed Platforms.ActiveCfg = Release-Eng|Any CPU 72 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|Mixed Platforms.Build.0 = Release-Eng|Any CPU 73 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|Win32.ActiveCfg = Release-Eng|Any CPU 74 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|x64.ActiveCfg = Release-Eng|Any CPU 75 | {04D8EFF9-5DF4-4B92-9F37-C1E8649C26C1}.Release-Eng|x86.ActiveCfg = Release-Eng|Any CPU 76 | EndGlobalSection 77 | GlobalSection(SolutionProperties) = preSolution 78 | HideSolutionNode = FALSE 79 | EndGlobalSection 80 | GlobalSection(ExtensibilityGlobals) = postSolution 81 | SolutionGuid = {C00D239C-4778-4F9F-A19B-3480D5B0EE9A} 82 | EndGlobalSection 83 | EndGlobal 84 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // このコードはツールによって生成されました。 4 | // ランタイム バージョン:4.0.30319.18052 5 | // 6 | // このファイルへの変更は、以下の状況下で不正な動作の原因になったり、 7 | // コードが再生成されるときに損失したりします。 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace Codeer.Friendly.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// ローカライズされた文字列などを検索するための、厳密に型指定されたリソース クラスです。 17 | /// 18 | // このクラスは StronglyTypedResourceBuilder クラスが ResGen 19 | // または Visual Studio のようなツールを使用して自動生成されました。 20 | // メンバーを追加または削除するには、.ResX ファイルを編集して、/str オプションと共に 21 | // ResGen を実行し直すか、または VS プロジェクトをビルドし直します。 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// このクラスで使用されているキャッシュされた ResourceManager インスタンスを返します。 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Codeer.Friendly.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// 厳密に型指定されたこのリソース クラスを使用して、すべての検索リソースに対し、 51 | /// 現在のスレッドの CurrentUICulture プロパティをオーバーライドします。 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// This has already been executed. An Async object can only be used once. To call an operation more than once, create a new Async object. に類似しているローカライズされた文字列を検索します。 65 | /// 66 | internal static string ErrorAsyncDuplicativeCall { 67 | get { 68 | return ResourceManager.GetString("ErrorAsyncDuplicativeCall", resourceCulture); 69 | } 70 | } 71 | 72 | /// 73 | /// Argument number {0} is incorrect. The namespace or class name could be a likely cause. Please double-check the syntax used for creating the argument. に類似しているローカライズされた文字列を検索します。 74 | /// 75 | internal static string ErrorDefinitionArgument { 76 | get { 77 | return ResourceManager.GetString("ErrorDefinitionArgument", resourceCulture); 78 | } 79 | } 80 | 81 | /// 82 | /// Argument number {0} is incorrect. The specified AppVar belongs to a separate AppFriend's variable pool. に類似しているローカライズされた文字列を検索します。 83 | /// 84 | internal static string ErrorDifferentAppFriendVar { 85 | get { 86 | return ResourceManager.GetString("ErrorDifferentAppFriendVar", resourceCulture); 87 | } 88 | } 89 | 90 | /// 91 | /// It is the object that has been disposed already. に類似しているローカライズされた文字列を検索します。 92 | /// 93 | internal static string ErrorDisposedObject { 94 | get { 95 | return ResourceManager.GetString("ErrorDisposedObject", resourceCulture); 96 | } 97 | } 98 | 99 | /// 100 | /// Invalid completion specification. This method should not generally be called. に類似しているローカライズされた文字列を検索します。 101 | /// 102 | internal static string ErrorInvalidCompleted { 103 | get { 104 | return ResourceManager.GetString("ErrorInvalidCompleted", resourceCulture); 105 | } 106 | } 107 | 108 | /// 109 | /// Illegal static function call. Operation information requires a type. に類似しているローカライズされた文字列を検索します。 110 | /// 111 | internal static string ErrorInvalidStaticCall { 112 | get { 113 | return ResourceManager.GetString("ErrorInvalidStaticCall", resourceCulture); 114 | } 115 | } 116 | 117 | /// 118 | /// An exception occurred inside the target application. 119 | ///[Message] 120 | ///{0} 121 | ///[Exception type] 122 | ///{1} 123 | ///[Error cause] 124 | ///{2} 125 | ///[Stack trace] 126 | ///{3} 127 | ///[Help] 128 | ///{4} に類似しているローカライズされた文字列を検索します。 129 | /// 130 | internal static string ExceptionInfoFormat { 131 | get { 132 | return ResourceManager.GetString("ExceptionInfoFormat", resourceCulture); 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Async.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Codeer.Friendly.Inside; 4 | using Codeer.Friendly.Inside.Protocol; 5 | using Codeer.Friendly.Properties; 6 | using System.Threading; 7 | 8 | namespace Codeer.Friendly 9 | { 10 | #if ENG 11 | /// 12 | /// Class for executing asynchronous operations in the target application of AppVar and AppFriends. 13 | /// When the operation has a return value or ref or out arguments, the values for these are stored in this class' object after the operation has finished. 14 | /// 15 | #else 16 | /// 17 | /// AppVar,AppFriendの対象アプリケーションへの操作を非同期で実行するためのクラスです。 18 | /// 操作に戻り値やref,outの引数がある場合、非同期実行完後、変数にそれぞれ値が格納されています。 19 | /// 20 | #endif 21 | public class Async 22 | { 23 | OperationTalker _operationTalker; 24 | AppVar _completedResult; 25 | bool _completed; 26 | 27 | #if ENG 28 | /// 29 | /// Indicates whether the operation has finished. 30 | /// 31 | #else 32 | /// 33 | /// 操作が完了したかを取得します。 34 | /// 35 | #endif 36 | public bool IsCompleted 37 | { 38 | get 39 | { 40 | if (_completed) 41 | { 42 | return true; 43 | } 44 | if (_completedResult == null) 45 | { 46 | return false; 47 | } 48 | return !(bool)_operationTalker.SendAndValueReceive(ProtocolType.IsEmptyVar, null, string.Empty, new object[] { _completedResult }); 49 | } 50 | } 51 | 52 | #if ENG 53 | /// 54 | /// Provides any exception that occurred during the execution. 55 | /// Returns null if the operation has not yet completed or if there was no exception. 56 | /// 57 | #else 58 | /// 59 | /// 実行中例外が発生していれば、例外を取得します。 60 | /// 発生していない場合、もしくは操作がまだ完了していない場合はnullが返ります。 61 | /// 62 | #endif 63 | public Exception ExecutingException 64 | { 65 | get 66 | { 67 | if (!IsCompleted) 68 | { 69 | return null; 70 | } 71 | ReturnInfo info = (ReturnInfo)_completedResult.Core; 72 | if (info.Exception == null) 73 | { 74 | return null; 75 | } 76 | return new FriendlyOperationException(info.Exception); 77 | } 78 | } 79 | 80 | #if ENG 81 | /// 82 | /// Constractor. 83 | /// 84 | #else 85 | /// 86 | /// コンストラクタ。 87 | /// 88 | #endif 89 | public Async() { } 90 | 91 | #if ENG 92 | /// 93 | /// Waits until the operation has finished. 94 | /// 95 | #else 96 | /// 97 | /// 操作が完了するまで待ちます。 98 | /// 99 | #endif 100 | public void WaitForCompletion() 101 | { 102 | while (!IsCompleted) 103 | { 104 | Thread.Sleep(10); 105 | } 106 | } 107 | 108 | #if ENG 109 | /// 110 | /// Marks the operation as complete. 111 | /// Should not typically be used. 112 | /// Used by implementations of this class or by libraries. 113 | /// 114 | #else 115 | /// 116 | /// 完了を設定します。 117 | /// 通常は使用しません。 118 | /// ライブラリ実装者が使用します。 119 | /// 120 | #endif 121 | public void SetCompleted() 122 | { 123 | //既に設定されている場合は呼び出してはならない。 124 | if (_completed) 125 | { 126 | throw new FriendlyOperationException(Resources.ErrorInvalidCompleted); 127 | } 128 | 129 | //既に通常使用されている場合は呼び出してはならない。 130 | if (_completedResult != null) 131 | { 132 | throw new FriendlyOperationException(Resources.ErrorInvalidCompleted); 133 | } 134 | _completed = true; 135 | } 136 | 137 | /// 138 | /// 初期化。 139 | /// 140 | /// 操作通信クラス。 141 | internal void Initialize(OperationTalker operationTalker) 142 | { 143 | //外部から完了をセットされた場合は呼び出し禁止。 144 | if (_completed) 145 | { 146 | throw new FriendlyOperationException(Resources.ErrorAsyncDuplicativeCall); 147 | } 148 | 149 | //再呼び出しは禁止。 150 | if (_completedResult != null) 151 | { 152 | throw new FriendlyOperationException(Resources.ErrorAsyncDuplicativeCall); 153 | } 154 | _operationTalker = operationTalker; 155 | } 156 | 157 | /// 158 | /// 非同期操作呼び出し。 159 | /// 160 | /// 操作タイプ情報。(オーバーロードの解決等に使用します。) 161 | /// 操作。 162 | /// 引数。 163 | /// 戻り値。 164 | internal AppVar Invoke(OperationTypeInfo operationTypeInfo, string operation, object[] arguments) 165 | { 166 | //完了情報格納バッファ宣言 167 | _completedResult = _operationTalker.SendAndReceive(ProtocolType.AsyncResultVarInitialize, null, string.Empty, new object[] { null }); 168 | 169 | //第一引数に入れて渡す。 170 | List arg = new List(); 171 | arg.Add(_completedResult); 172 | arg.AddRange(arguments); 173 | 174 | //呼び出し。 175 | AppVar returnValue = _operationTalker.SendAndReceive(ProtocolType.AsyncOperation, operationTypeInfo, operation, arg.ToArray()); 176 | GC.KeepAlive(_operationTalker); 177 | 178 | //戻り値格納変数を返す。 179 | return returnValue; 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Inside/FriendlyTalker.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Collections.Generic; 4 | using Codeer.Friendly.Inside.Protocol; 5 | using Codeer.Friendly.Properties; 6 | using System.Globalization; 7 | 8 | namespace Codeer.Friendly.Inside 9 | { 10 | /// 11 | /// コミュニケーター。 12 | /// 13 | static class FriendlyTalker 14 | { 15 | /// 16 | /// 戻り値をAppVarで取得する通信。 17 | /// 18 | /// 呼び出し元。 19 | /// アプリケーションとの接続者。 20 | /// 通信タイプ。 21 | /// 操作タイプ情報。 22 | /// 変数アドレス。 23 | /// タイプフルネーム。 24 | /// 操作名称。 25 | /// 引数。 26 | /// 変数。 27 | internal static AppVar SendAndVarReceive(object invoker, IFriendlyConnector friendlyConnector, ProtocolType protocolType, 28 | OperationTypeInfo operationTypeInfo, VarAddress varAddress, string typeFullName, string operation, object[] arguments) 29 | { 30 | object value = SendAndValueReceive(invoker, friendlyConnector, protocolType, operationTypeInfo, varAddress, typeFullName, operation, arguments); 31 | VarAddress retHandle = value as VarAddress; 32 | return (retHandle == null) ? (null) : (new AppVar(friendlyConnector, retHandle)); 33 | } 34 | 35 | /// 36 | /// 戻り値を値で取得する通信処理。 37 | /// 通信基本形。 38 | /// 39 | /// 呼び出し元。 40 | /// アプリケーションとの接続者。 41 | /// 通信タイプ。 42 | /// 操作タイプ情報。 43 | /// 変数アドレス。 44 | /// タイプフルネーム。 45 | /// 操作名称。 46 | /// 引数。 47 | /// 値。 48 | internal static object SendAndValueReceive(object invoker, IFriendlyConnector friendlyConnector, ProtocolType protocolType, 49 | OperationTypeInfo operationTypeInfo, VarAddress varAddress, string typeFullName, string operation, object[] arguments) 50 | { 51 | //配列の場合の調整 52 | arguments = AdjustArrayArgs(arguments); 53 | 54 | ReturnInfo ret = friendlyConnector.SendAndReceive(new ProtocolInfo(protocolType, operationTypeInfo, varAddress, typeFullName, operation, ConvertAppVar(friendlyConnector, arguments))); 55 | GC.KeepAlive(invoker); 56 | GC.KeepAlive(friendlyConnector); 57 | for (int i = 0; i < arguments.Length; i++) 58 | { 59 | if (arguments[i] != null) 60 | { 61 | GC.KeepAlive(arguments[i]); 62 | } 63 | } 64 | if (ret.Exception != null) 65 | { 66 | throw new FriendlyOperationException(ret.Exception); 67 | } 68 | return ret.ReturnValue; 69 | } 70 | 71 | /// 72 | /// object[]以外の場合はobject[]でくるんでやる 73 | /// 74 | /// 引数 75 | /// 調整した引数 76 | private static object[] AdjustArrayArgs(object[] arguments) 77 | { 78 | if (arguments.GetType() != typeof(object[])) 79 | { 80 | return new object[] { arguments }; 81 | } 82 | return arguments; 83 | } 84 | 85 | /// 86 | /// AppVarがあれば、Varハンドルに変換。 87 | /// 88 | /// アプリケーションとの接続者。 89 | /// 引数。 90 | /// 変換結果。 91 | static object[] ConvertAppVar(IFriendlyConnector friendlyConnector, object[] arguments) 92 | { 93 | object[] argsTmp = new object[arguments.Length]; 94 | for (int i = 0; i < arguments.Length; i++) 95 | { 96 | argsTmp[i] = ConvertAppVar(friendlyConnector, arguments[i], i); 97 | } 98 | return argsTmp; 99 | } 100 | 101 | /// 102 | /// AppVarであれば、Varハンドルに変換。 103 | /// 104 | /// アプリケーションとの接続者。 105 | /// 引数のインデックス。 106 | /// 引数。 107 | /// 変換結果。 108 | static object ConvertAppVar(IFriendlyConnector friendlyConnector, object arg, int index) 109 | { 110 | AppVar appVar = null; 111 | { 112 | IAppVarSelf selft = arg as IAppVarSelf; 113 | if (selft != null) 114 | { 115 | appVar = selft.CodeerFriendlyAppVar; 116 | } 117 | } 118 | if (appVar == null) 119 | { 120 | IAppVarOwner owner = arg as IAppVarOwner; 121 | if (owner != null) 122 | { 123 | appVar = owner.AppVar; 124 | } 125 | } 126 | if (appVar == null) 127 | { 128 | appVar = arg as AppVar; 129 | } 130 | if (appVar == null) 131 | { 132 | if ((arg as IDefinition) != null) 133 | { 134 | throw new FriendlyOperationException(string.Format(CultureInfo.CurrentCulture, Resources.ErrorDefinitionArgument, index + 1)); 135 | } 136 | return arg; 137 | } 138 | 139 | //違う変数プールに属する変数は使用できない 140 | if (!object.ReferenceEquals(friendlyConnector.Identity, appVar.FriendlyConnector.Identity)) 141 | { 142 | throw new FriendlyOperationException(string.Format(CultureInfo.CurrentCulture, Resources.ErrorDifferentAppFriendVar, index + 1)); 143 | } 144 | return appVar.VarAddress; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Codeer.Friendly.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.30703 7 | 2.0 8 | {8CD64A7C-848F-4466-B5F4-8C4842A6316D} 9 | Library 10 | Properties 11 | Codeer.Friendly 12 | Codeer.Friendly 13 | v2.0 14 | 512 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | bin\Debug\Codeer.Friendly.XML 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | bin\Release\Codeer.Friendly.XML 34 | 35 | 36 | true 37 | 38 | 39 | Codeer.Friendly.snk 40 | 41 | 42 | bin\Release-Eng\ 43 | TRACE;ENG 44 | bin\Release\Codeer.Friendly.XML 45 | true 46 | pdbonly 47 | AnyCPU 48 | prompt 49 | false 50 | false 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 | True 86 | True 87 | Resources.resx 88 | 89 | 90 | Resources.ja.resx 91 | True 92 | True 93 | 94 | 95 | 96 | 97 | ResXFileCodeGenerator 98 | Resources.Designer.cs 99 | 100 | 101 | ResXFileCodeGenerator 102 | Resources.ja.Designer.cs 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | if $(ConfigurationName) == Release mkdir "../../../ReleaseBinary" 111 | if $(ConfigurationName) == Release-Eng mkdir "../../../ReleaseBinary" 112 | if $(ConfigurationName) == Release mkdir "../../../ReleaseBinary/ja" 113 | if $(ConfigurationName) == Release Copy "$(TargetPath)" "../../../ReleaseBinary/$(TargetFileName)" 114 | if $(ConfigurationName) == Release-Eng Copy "$(TargetDir)\$(TargetName).xml" "../../../ReleaseBinary/$(TargetName).xml" 115 | if $(ConfigurationName) == Release Copy "$(TargetDir)\$(TargetName).xml" "../../../ReleaseBinary/ja/$(TargetName).xml" 116 | if $(ConfigurationName) == Release Copy "$(TargetDir)\ja\$(TargetName).resources.dll" "../../../ReleaseBinary/ja/$(TargetName).resources.dll" 117 | 118 | 125 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Properties/Resources.ja.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 引数に複数のAsyncが見つかりました。 122 | Asyncは一度の呼び出しで一つしか指定することができません。 123 | 124 | 125 | インスタンスの生成時にはAsyncクラスを指定することはできません。 126 | 127 | 128 | 引数に複数のOperationTypeInfoが見つかりました。 129 | OperationTypeInfoは一度の呼び出しで一つしか指定することができません。 130 | 131 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/AppFriendExtensions.cs: -------------------------------------------------------------------------------- 1 | using Codeer.Friendly.Dynamic.Inside; 2 | using System; 3 | 4 | namespace Codeer.Friendly.Dynamic 5 | { 6 | #if ENG 7 | /// 8 | /// AppFriend extension methods to provide dynamic functionality. 9 | /// 10 | #else 11 | /// 12 | /// AppFriendを拡張します。 13 | /// 14 | #endif 15 | public static class AppFriendExtensions 16 | { 17 | #if ENG 18 | /// 19 | /// Helper method for generating a DynamicAppType for the specified type. 20 | /// 21 | /// 22 | /// dynamic controlType = app.Type<Control>(); 23 | /// 24 | /// Type. 25 | /// Application manipulation object. 26 | /// DynamicAppType. 27 | #else 28 | /// 29 | /// 指定のタイプのDynamicAppTypeを生成するためのヘルパメソッドです。 30 | /// 31 | /// 32 | /// ・System.Windows.Forms.ControlクラスのDynamicAppTypeを作成する場合 33 | ///  dynamic controlType = app.Type<Control>(); 34 | /// 35 | /// タイプ。 36 | /// アプリケーション操作クラス。 37 | /// DynamicAppType。 38 | #endif 39 | public static dynamic Type(this AppFriend app) 40 | { 41 | return new DynamicAppType(app, typeof(T).FullName); 42 | } 43 | 44 | #if ENG 45 | /// 46 | /// Helper method for generating a DynamicAppType for the specified type. 47 | /// 48 | /// 49 | /// dynamic controlType = app.Type(typeof(Control)); 50 | /// 51 | /// Application manipulation object. 52 | /// Type. 53 | /// DynamicAppType. 54 | #else 55 | /// 56 | /// 指定の名前のDynamicAppTypeを生成するヘルパメソッドです。 57 | /// 58 | /// 59 | /// ・System.Windows.Forms.ControlクラスのDynamicAppTypeを作成する場合 60 | ///  dynamic controlType = app.Type(typeof(Control)); 61 | /// 62 | /// アプリケーション操作クラス。 63 | /// タイプ。 64 | /// DynamicAppType。 65 | #endif 66 | public static dynamic Type(this AppFriend app, Type type) 67 | { 68 | if (type == null) 69 | { 70 | throw new ArgumentNullException("type"); 71 | } 72 | return new DynamicAppType(app, type.FullName); 73 | } 74 | 75 | #if ENG 76 | /// 77 | /// Helper method for generating a DynamicAppType for the specified type. 78 | /// 79 | /// 80 | /// dynamic controlType = app.Type("System.Windows.Forms.Control"); 81 | /// 82 | /// Application manipulation object. 83 | /// Name space or TypeFullName. 84 | /// DynamicAppType. 85 | #else 86 | /// 87 | /// 指定の名前のDynamicAppTypeを生成するヘルパメソッドです。 88 | /// 89 | /// 90 | /// ・System.Windows.Forms.ControlクラスのDynamicAppTypeを作成する場合 91 | ///  dynamic controlType = app.Type("System.Windows.Forms.Control"); 92 | /// 93 | /// アプリケーション操作クラス。 94 | /// ネームスペースもしくはタイプフルネーム。 95 | /// DynamicAppType。 96 | #endif 97 | public static dynamic Type(this AppFriend app, string name) 98 | { 99 | return new DynamicAppType(app, name); 100 | } 101 | 102 | #if ENG 103 | /// 104 | /// Helper method to generate an empty DynamicAppType. 105 | /// You should follow a call to Type() with the fully qualified namespace and classname of the type you want to access. 106 | /// 107 | /// 108 | /// app.Type().System.Windows.Forms.Control.FromHandle(handle); 109 | /// 110 | /// Application manipulation object. 111 | /// DynamicAppType。 112 | #else 113 | /// 114 | /// 空のDynamicAppTypeを生成するヘルパメソッドです。 115 | /// この後にネームスペースとタイプを続けてください。 116 | /// 117 | /// 118 | /// ・System.Windows.Forms.ControlクラスのDynamicAppTypeを作成する場合 119 | ///  dynamic controlType = app.Type().System.Windows.Forms.Control; 120 | /// 121 | /// アプリケーション操作クラス。 122 | /// DynamicAppType。 123 | #endif 124 | public static dynamic Type(this AppFriend app) 125 | { 126 | return new DynamicAppType(app); 127 | } 128 | 129 | #if ENG 130 | /// 131 | /// Copies indicated object into the target application and returns a DynamicAppVar to access it. 132 | /// 133 | /// Application manipulation object. 134 | /// Object to be sent (must be serializable, AppVar, or DynamicAppVar). 135 | /// DynamicAppVar. 136 | #else 137 | /// 138 | /// テスト対象アプリケーション内に指定のオブジェクトをコピーし、その変数を操作するDynamicAppVarを返します。 139 | /// 140 | /// アプリケーション操作クラス。 141 | /// 初期化オブジェクト(シリアライズ可能なオブジェクトもしくはAppVar、DynamicAppVarであること)。 142 | /// DynamicAppVar。 143 | #endif 144 | public static dynamic Copy(this AppFriend app, object obj) 145 | { 146 | return app.Dim(obj).Dynamic(); 147 | } 148 | 149 | #if ENG 150 | /// 151 | /// Declares a null variable in the target application and returns a DynamicAppVar to access it. 152 | /// 153 | /// Application manipulation object. 154 | /// DynamicAppVar. 155 | #else 156 | /// 157 | /// テスト対象アプリケーション内にnullの変数を宣言し、その変数を操作するDynamicAppVarを返します。 158 | /// 159 | /// アプリケーション操作クラス。 160 | /// DynamicAppVar。 161 | #endif 162 | public static dynamic Null(this AppFriend app) 163 | { 164 | return app.Dim().Dynamic(); 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | Multiple Async arguments were found. Only one Async argument can be specified per call. 122 | 123 | 124 | Async cannot be used when instantiating an object. 125 | 126 | 127 | Multiple OperationTypeInfo arguments were found. Only one OperationTypeInfo argument can be specified per call. 128 | 129 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Properties/Resources.ja.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | すでに実行されています。Asyncクラスの実行は一度だけです。複数回呼び出す場合は、再度Asyncクラスを生成してください。 122 | 123 | 124 | 第{0}引数が不正です。 125 | 「ネームスペース」もしくは「クラス名称」と推測されます。引数を生成した構文を確認してください。 126 | 127 | 128 | 第{0}引数が不正です。 129 | 引数に使用されたAppVarの中に、異なるAppFriendの管理する変数プールに属するAppVarがあります。 130 | 131 | 132 | 既に破棄されたオブジェクトです。 133 | 134 | 135 | 不正な終了指定です。通常このメソッドは使用しません。 136 | 137 | 138 | 不正なstatic呼び出しです。操作情報には型が必要です。 139 | 140 | 141 | 対象アプリケーション内部で例外が発生しました。 142 | [メッセージ] 143 | {0} 144 | [例外タイプ] 145 | {1} 146 | [エラーの原因] 147 | {2} 148 | [スタックトレース] 149 | {3} 150 | [ヘルプ] 151 | {4} 152 | 153 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 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 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | This has already been executed. An Async object can only be used once. To call an operation more than once, create a new Async object. 122 | 123 | 124 | Argument number {0} is incorrect. The namespace or class name could be a likely cause. Please double-check the syntax used for creating the argument. 125 | 126 | 127 | Argument number {0} is incorrect. The specified AppVar belongs to a separate AppFriend's variable pool. 128 | 129 | 130 | It is the object that has been disposed already. 131 | 132 | 133 | Invalid completion specification. This method should not generally be called. 134 | 135 | 136 | Illegal static function call. Operation information requires a type. 137 | 138 | 139 | An exception occurred inside the target application. 140 | [Message] 141 | {0} 142 | [Exception type] 143 | {1} 144 | [Error cause] 145 | {2} 146 | [Stack trace] 147 | {3} 148 | [Help] 149 | {4} 150 | 151 | -------------------------------------------------------------------------------- /TestAutomationDesign.jp.md: -------------------------------------------------------------------------------- 1 | Driver/Scenarioパターン 2 | ======== 3 | 4 | ここでは我々がWindowsアプリケーションの自動システムテストを構築するときに使っている設計方針を紹介します。 5 | 自動テストを作成するということはテストを自動で実行するソフトウェアを開発するということです。 6 | そして対象のアプリと同じだけのライフサイクルがあり多くの場合は長期にわたってメンテナンスしていきます。 7 | 以下のポイントに気を付けて費用対効果を高める必要があります。 8 | 9 | + 作成効率 10 | + メンバーのアサイン 11 | + メンテナンス性 12 | 13 | 基本方針はDriverとScenarioに分けて実装するというものです。 14 | これはWebアプリをSeleniumでテストするときのページオブジェクトパターンに似ています。 15 | Driver他プロセスを操作するモジュールでScenarioはテストを記述するモジュールです。 16 | ![DriverAndScenario.jp.jpg](Img/DriverAndScenario.jp.jpg) 17 | 18 | # Scenario 19 | Scenarioはテストケースです。アプリケーションの操作とその後の状態の検証を行います。 20 | これは通常はテストチームが手動で行っていたもので、それをコードで表現したものです。 21 | Scenarioはメイン業務がテストの人でも記述できるようにすべきです。 22 | 制御文は可能な限り入れず理解しやすいAPIでの操作とAssertで記述できるようにします。 23 | DriverとScenarioでは圧倒的にScenarioのボリュームが多くなります。 24 | またScenarioを記述するときは技術的なことよりもアプリ/テストの仕様に集中できるようにする必要があります。 25 | サンプルコードです。 26 | ```cs 27 | [TestMethod] 28 | public void Sample() 29 | { 30 | //操作 31 | //ボタン押下、テキスト入力などのシンプルなコントロール操作のみ 32 | var mainWindow = _app.AttachMainWindow(); 33 | mainWindow.Add.EmulateClick(); 34 | var entryControl = mainWindow.AttachEntryControl(); 35 | entryControl.Name.EmulateChangeText("ishikawa"); 36 | entryControl.Birthday.EmulateChangeDate(new DateTime(1977, 1, 7)); 37 | entryControl.Age.EmulateChangeValue(42); 38 | entryControl.email.EmulateChangeText("ishikawa@xxx.com"); 39 | entryControl.Preferredlanguage.EmulateChangeSelectedIndex(2); 40 | entryControl.Man.EmulateCheck(true); 41 | entryControl.Entry.EmulateClick(); 42 | 43 | //Assert 44 | mainWindow.DataGrid.GetCellText(0, 0).Is("ishikawa"); 45 | mainWindow.DataGrid.GetCellText(0, 1).Is("ishikawa@xxx.com"); 46 | mainWindow.DataGrid.GetCellText(0, 2).Is("C#"); 47 | mainWindow.DataGrid.GetCellText(0, 3).Is("Man"); 48 | mainWindow.DataGrid.GetCellText(0, 4).Is("42"); 49 | mainWindow.DataGrid.GetCellText(0, 5).Is("1977/01/07"); 50 | } 51 | ``` 52 | このコードの特徴は複雑な制御文がなく、上から下に操作とその後の判定を書いていることです。 53 | トレーニングは必要ですが専門職のプログラマーでなくても書くことができます。 54 | 自動テスト設計ではこのように要員の確保のしやすさも考慮に入れる必要があります。 55 | 画面要素の特定などはここには出てきません。 56 | 外部仕様だけで記述しています。 57 | そのため内部の設計やコントロールの位置が変わったくらいではシナリオのメンテナンスは発生しません。 58 | もちろん外部仕様が変わってしまった場合は書き直す必要があります。 59 | 60 | # Driver (ControlDriver, WindowDriver/UserControlDriver) 61 | Driverは逆にあまりテストのことは考えずに対象プロセスを制御することに集中します。 62 | そして技術的なことやアプリの内部仕様に関してはこのレイヤに隠蔽します。 63 | Driverはさらに大きくは二種類に分かれます。 64 | ControlDriverとWindowDriver/UserControlDriverです。 65 | ControlDriver,WindowDriver,UserControlDriverなら3種類じゃないのかと感じられると思いますが、 66 | WindowDriver/UserControlDriverは同一の性質を持ちます。 67 | それはWindowとUserControlが同じ性質を持つのと同じです。 68 | WindowとUserControlはともに子となる要素(Control/UserControl)をデザイナやXamlで並べていきます 69 | WindowDriver/UserControlDriverは子となる要素を特定/取得することがその責務です。 70 | 対してContorlはそれ自体が独立した機能を持っています。 71 | 多くの場合は汎用性が高く様々なWindow/UserControlで使われます。 72 | ControlDriverは対象のコントロールの機能を操作することが責務です。 73 | そのしてWindowDriver/UserControlDriverのプロパティとして特定した要素を操作することに使われます 74 | 75 | ![Drivers.png](Img/Drivers.png) 76 | 77 | ## ControlDriver 78 | ControlDriverは Button, ListView, TreeView などの基本的なコントロール単位での操作を提供します。 79 | ControlDriverは汎用的なもので使いまわすことができます。 80 | 一般的なコントロールに関してはFriendlyの関連ライブラリで既に実装したものがありますのでそれをご利用ください。 81 | プロジェクト固有のコントロールや3rdパーティ製のコントロールに関してはそれぞれで実装する必要があります。 82 | ControlDriverの実装は難易度が高いです。 83 | それぞれのControlに関しての知識が求められます。 84 | ただ、それがあればFriendlyの基本機能を使えばほとんどのものが実装可能です。 85 | 86 | 例えばこのようなカスタムコントロールがあった場合(WPFにはNumericUpDownはありません) 87 | 88 | ![NumericUpDwon.png](Img/NumericUpDwon.png) 89 | 90 | ```cs 91 | public class NumericUpDownControl : Control 92 | { 93 | public TextBox ValueTextBox { get; set; } 94 | public RepeatButton UpButton { get; set; } 95 | public RepeatButton DownButton { get; set; } 96 | 97 | //以下省略 98 | ``` 99 | 100 | そのコントロールドライバは以下のようになります。 101 | Friendlyの基本機能を使えば問題なく作成できます。 102 | ```cs 103 | using Codeer.Friendly; 104 | using Codeer.Friendly.Dynamic; 105 | 106 | namespace Driver.CustomDrivers 107 | { 108 | public class WPFNumericUpDownDriver : IAppVarOwner 109 | { 110 | public AppVar AppVar { get; } 111 | 112 | public WPFNumericUpDownDriver(AppVar src) => AppVar = src; 113 | 114 | public int Value => this.Dynamic().Value; 115 | 116 | public void EmulateChangeValue(int value) 117 | { 118 | var textBox = this.Dynamic().ValueTextBox; 119 | textBox.Focus(); 120 | textBox.Text = value.ToString(); 121 | } 122 | } 123 | } 124 | ``` 125 | ## WindowDriver/UserControlDriver 126 | WindowDriver/UserControlDriverは各Window/Form/UserControl/Pageのドライバです。 127 | 128 | WindowやForm自体は通常ButtonやTextBoxなどのControlをレイアウトして作成されます。 129 | そのためWindowDriverはレイアウトされたControlを特定/取得し、ControlDriverでラップして提供することが目的となります。 130 | WindowDriver/UserControlDriverは対象の性質上使いまわすことはほとんどなく、対象のWindow/UserControlに対し一点ものになります。 131 | 132 | WindowDriverを実装する際は各Windowの情報が必要になります。 133 | 具体的にはフィールド名やWPFならバインディング名などControlを特定するための情報です。 134 | ここで必要なものは.Netの知識よりそのアプリの実装/設計に関する情報です。 135 | WinFormsならフィールドで簡単に特定できることが多いのですが、WPFではx:nameがついていないことも多く、Win32の場合はそもそも.netではないのでフィールドは使えません。 136 | そのような場合のためにライブラリでいくつか特定するためのメソッドを用意しているのでそれを使ってください。 137 | これでもダメな場合でもFriendlyの基本を理解すれば自分で新たな特定方法を作ることができます。 138 | + [WPF](https://github.com/Roommetro/Friendly.WPFStandardControls/) 139 | + [Win32](https://github.com/Codeer-Software/Friendly.Windows.Grasp) 140 | 141 | WindowDriverの例です。 142 | 143 | ![WindowDriverTarget.png](Img/WindowDriverTarget.png) 144 | ```cs 145 | using Codeer.Friendly.Dynamic; 146 | using Codeer.Friendly.Windows; 147 | using Codeer.Friendly.Windows.Grasp; 148 | using RM.Friendly.WPFStandardControls; 149 | 150 | namespace Driver.Windows 151 | { 152 | public class MainWindowDriver 153 | { 154 | //MainWindow自体を操作するためのWindowControl 155 | public WindowControl Core { get; } 156 | 157 | //タイプで特定 158 | public WPFMenuBase Menu => Core.LogicalTree().ByType("System.Windows.Controls.Menu").Single().Dynamic(); 159 | 160 | //フィールドで特定 161 | public WPFTextBox Name => thid.Dynamic()._textBoxName; 162 | 163 | //バインディングで特定 164 | public WPFComboBox Preferredlanguage => Core.LogicalTree().ByBinding("LanguageSearch.Value").Single().Dynamic(); 165 | public WPFDataGrid DataGrid => Core.LogicalTree().ByBinding("SelectedItem.Value").Single().Dynamic(); 166 | 167 | //文字列で特定 168 | public WPFButtonBase Search => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Search").Single().Dynamic(); 169 | public WPFButtonBase Add => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Add").Single().Dynamic(); 170 | public WPFButtonBase Delete => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Delete").Single().Dynamic(); 171 | 172 | public MainWindowDriver(WindowControl core) => Core = core; 173 | } 174 | } 175 | ``` 176 | 177 | UserControlDriverもほぼ同じ実装方法になります。 178 | 179 | ![UserControlDriverTarget.png](Img/UserControlDriverTarget.png) 180 | ```cs 181 | using Codeer.Friendly; 182 | using Codeer.Friendly.Dynamic; 183 | using RM.Friendly.WPFStandardControls; 184 | using Driver.CustomDrivers; 185 | using System.Windows.Controls; 186 | 187 | namespace Driver.Windows 188 | { 189 | public class EntryControlDriver 190 | { 191 | //EntryControl自体を操作するためのWPFUserControl 192 | public WPFUserControl Core { get; } 193 | 194 | public WPFTextBox Name => Core.Dynamic()._textBoxName; 195 | public WPFContextMenu NameContextMenu => new WPFContextMenu{Target = Name.AppVar}; 196 | public WPFTextBox email => Core.LogicalTree().ByBinding("Mail.Value").Single().Dynamic(); 197 | public WPFContextMenu emailContextMenu => new WPFContextMenu{Target = email.AppVar}; 198 | public WPFComboBox Preferredlanguage => Core.LogicalTree().ByBinding("Language.Value").Single().Dynamic(); 199 | public WPFToggleButton Man => Core.LogicalTree().ByBinding("IsMan.Value").Single().Dynamic(); 200 | public WPFToggleButton Woman => Core.LogicalTree().ByBinding("IsWoman.Value").Single().Dynamic(); 201 | public WPFCalendar Birthday => Core.LogicalTree().ByBinding("BirthDay.Value").Single().Dynamic(); 202 | public WPFNumericUpDownDriver Age => Core.LogicalTree().ByBinding("Age.Value").Single().Dynamic(); 203 | public WPFButtonBase Entry => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Entry").Single().Dynamic(); 204 | public WPFButtonBase Cancel => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Cancel").Single().Dynamic(); 205 | 206 | public EntryControlDriver(AppVar core) 207 | => Core = new WPFUserControl(core); 208 | } 209 | } 210 | ``` 211 | 212 | ### Attach 213 | Attachは対象のWindow/UserControlを検索し、WindowDriver/UserControlDriverを生成するコードになります。 214 | これもシナリオ中に書くと可読性/メンテナンス性が下がるためドライバの一部として実装します。 215 | 我々はこれを拡張メソッドで実装しています。 216 | 引数で渡した対象から目的のWindow/UserControlを検索しDriverを生成しています。 217 | 下記の例ではWindowsAppFriend(アプリケーション全体)からDemoApp.Views.MainWindowを.Netの型名称を使って検索しMainWindowDriverを生成しています。 218 | 219 | ```cs 220 | //Attachを拡張メソッドとして提供する 221 | public static class MainWindowDriverExtensions 222 | { 223 | //アプリケーション全体から検索 224 | public static MainWindowDriver AttachMainWindow(this WindowsAppFriend app) 225 | => app.WaitForIdentifyFromTypeFullName("DemoApp.Views.MainWindow").Dynamic(); 226 | } 227 | ``` 228 | ```cs 229 | [TestMethod] 230 | public void Sample() 231 | { 232 | //Attach 233 | var mainWindow = _app.AttachMainWindow(); 234 | 235 | //以下操作 236 | mainWindow.Name.EmulateChangeText("ishikawa"); 237 | ``` 238 | 239 | UserControlの場合はAttachを使わずに、それを保持する親のWindowDriverのプロパティとして取得することも多いです。 240 | ```cs 241 | public class MainWindowDriver 242 | { 243 | public WindowControl Core { get; } 244 | 245 | //親ウィンドウのプロパティとして取得 246 | public EntryControlDriver EntryControl => Core.Dynamic()._entryControl; 247 | 248 | public MainWindow_Driver(WindowControl core) => Core = core; 249 | } 250 | ``` 251 | Attachを作成する場合もあります。 252 | どちらを使っても構いません。 253 | UserControlが常に表示されている場合はWindowDriverのプロパティを使い、条件によって表示/非表示が切り替わるものに関してはAttachを使うことが多いです。 254 | 以下の例はMainWindowDriver(DemoApp.Views.MainWindow)からDemoApp.Views.EntryControlを検索して存在していればEntryControlDriverを生成するコードです。 255 | ```cs 256 | public static class EntryControlDriverExtensions 257 | { 258 | //MainWindowDriver(DemoApp.Views.MainWindow)から検索 259 | public static EntryControlDriver AttachEntryControl(this MainWindowDriver window) 260 | => window.Core.VisualTree().ByType("DemoApp.Views.EntryControl").SingleOrDefault()?.Dynamic(); 261 | } 262 | ``` 263 | UserControlでもアプリケーション全体から検索することもあります。ドッキングタイプのアプリの場合は親ウィンドウが定まりません。 264 | 以下の例ではアプリケーション中の全てのトップレベルウィンドウの中からDemoApp.OutputWindowを検索し存在していればOutputWindowDriverを生成しています。 265 | ```cs 266 | public static class OutpuWindowDriverExtensions 267 | { 268 | [UserControlDriverIdentify] 269 | public static OutpuWindowDriver AttachOutpuWindow(this WindowsAppFriend app) 270 | => app.GetTopLevelWindows().SelectMany(e => e.GetFromTypeFullName("DemoApp.OutputWindow")).FirstOrDefault()?.Dynamic(); 271 | } 272 | ``` 273 | 274 | 検索条件を引数で渡すこともあります。同一タイプで複数存在している時にタイトルで特定するなどです。特定方法は状況によって最適なもににします。 275 | ```cs 276 | public static class DataWindowDriverExtensions 277 | { 278 | public static DataWindowDriver AttachDataWindow(this WindowsAppFriend app, string title) 279 | => app.GetFromTypeFullName("DemoApp.Views.DataWindow").Where(e => e.GetWindowText() == title).Dynamic(); 280 | } 281 | ``` 282 | 283 | ## プロジェクト構成 284 | さらにそれぞれが操作時に対象プロセス内部で実行させる処理を実装するなら、その処理は別のdllに分ける方がおすすめです。 285 | これは実装効率のためです。対象プロセスにロードさせるとそのプロセスが稼働中の間はそのdllを再度コンパイルすることができません。 286 | 対象プロセスにロードさせる処理は比較的少ないので、分けておくとプロセスが稼働している間にコンパイルすることができます。 287 | 288 | 最終的にはこのようなプロジェクト構成がおすすめです。 289 | 290 | ![Dlls.png](Img/Dlls.png) 291 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /TestAutomationDesign.md: -------------------------------------------------------------------------------- 1 | Automatic test design for Windows applications. 2 | ======== 3 | 4 | This document introduces the design principles we use when building automated system tests for Windows applications.
5 | Creating an automated test means developing software that automatically runs the test.
6 | And it has the same life cycle as the target application, and in many cases we will maintain it for a long time.
7 | You need to be aware of the following points to be cost effective.
8 | 9 | + Creation efficiency 10 | + Member assignment 11 | + Maintainability 12 | 13 | The basic policy is to implement it separately for Driver and Scenario.
14 | This is similar to the page object pattern when testing a web app with Selenium.
15 | Driver is a module that manipulates other processes. Scenario is a module that describes tests.
16 | ![DriverAndScenario.jpg](Img/DriverAndScenario.jpg) 17 | 18 | # Scenario 19 | Scenario should be able to be written even if the main task is a test person.
20 | When writing scenarios, use control statements as little as possible, and use simple API operations and Assert only.
21 | Compared to Driver and Scenario, the volume of Scenario is overwhelmingly large.
22 | Also, when writing a Scenario, you need to be able to focus on the app/test specifications rather than the technical things.
23 | Here is the sample code. 24 | ```cs 25 | [TestMethod] 26 | public void Sample() 27 | { 28 | //manipulation 29 | var mainWindow = _app.AttachMainWindow(); 30 | var allDisplayControl = mainWindow.AttachAllDisplayControl(); 31 | allDisplayControl.Add.EmulateClick(); 32 | var entryControl = mainWindow.AttachEntryControl(); 33 | entryControl.Name.EmulateChangeText("ishikawa"); 34 | entryControl.Birthday.EmulateChangeDate(new DateTime(1977, 1, 7)); 35 | entryControl.Age.EmulateChangeValue(42); 36 | entryControl.email.EmulateChangeText("ishikawa@xxx.com"); 37 | entryControl.Preferredlanguage.EmulateChangeSelectedIndex(2); 38 | entryControl.Man.EmulateCheck(true); 39 | entryControl.Entry.EmulateClick(); 40 | 41 | //Assert 42 | allDisplayControl.DataGrid.GetCellText(0, 0).Is("ishikawa"); 43 | allDisplayControl.DataGrid.GetCellText(0, 1).Is("ishikawa@xxx.com"); 44 | allDisplayControl.DataGrid.GetCellText(0, 2).Is("C#"); 45 | allDisplayControl.DataGrid.GetCellText(0, 3).Is("Man"); 46 | allDisplayControl.DataGrid.GetCellText(0, 4).Is("42"); 47 | allDisplayControl.DataGrid.GetCellText(0, 5).Is("1977/01/07"); 48 | } 49 | ``` 50 | The feature of this code is that there are no complicated control statements, only the operation and the judgment that are written from top to bottom.
51 | Training is required, but non-professional programmers can write.
52 | Automated test design must also take into account the availability of personnel.
53 | The identification of screen elements does not appear here.
54 | It is described only by external specifications.
55 | Therefore, maintenance of the scenario does not occur even if the internal design changes.
56 | Of course, if the external specifications have changed, you need to rewrite.
57 | 58 | # Driver 59 | The Driver concentrates on controlling the target process without thinking too much about testing.
60 | And it hides technical matters and internal specifications of the application in this layer.
61 | There are two main types of drivers.
62 | Control Driver and Window Driver.
63 | 64 | ## ControlDriver 65 | Control Driver provides basic operations for each control such as Button, ListView, TreeView.
66 | The Control Driver is a general-purpose type and can be reused.
67 | As for general controls, there is one already implemented in Friendly's related library, so please use it.
68 | Project specific controls and 3rd party controls must be implemented by each.
69 | Implementing ControlDriver is difficult.
70 | Knowledge of each Control is required.
71 | However, if you have it, you can implement most of it using the basic functions of Friendly.
72 |
73 | For example if you have a custom control like this (There is no NumericUpDown in WPF)
74 | ![NumericUpDwon.png](Img/NumericUpDwon.png) 75 | 76 | ```cs 77 | public class NumericUpDownControl : Control 78 | { 79 | public TextBox ValueTextBox { get; set; } 80 | public RepeatButton UpButton { get; set; } 81 | public RepeatButton DownButton { get; set; } 82 | 83 | //Omitted below 84 | ``` 85 | 86 | The control driver for this is:
87 | You can create it without problems if you use the basic functions of Friendly.
88 | ```cs 89 | using Codeer.Friendly; 90 | using Codeer.Friendly.Dynamic; 91 | 92 | namespace Driver.CustomDrivers 93 | { 94 | public class WPFNumericUpDownDriver : IAppVarOwner 95 | { 96 | public AppVar AppVar { get; } 97 | 98 | public WPFNumericUpDownDriver(AppVar src) => AppVar = src; 99 | 100 | public int Value => this.Dynamic().Value; 101 | 102 | public void EmulateChangeValue(int value) 103 | { 104 | var textBox = this.Dynamic().ValueTextBox; 105 | textBox.Focus(); 106 | textBox.Text = value.ToString(); 107 | } 108 | } 109 | } 110 | ``` 111 | ## WindowDriver 112 | This is the Window/Form/UserControl driver.
113 | (In detail, it is UserControlDriver, but here I will collectively call it WindowDriver.)
114 |
115 | Window and Form itself are usually created by laying out Controls such as Button and TextBox.
116 | Therefore, the purpose of WindowDriver is to get the laid out Control and wrap it with ControlDriver to provide it.
117 | The WindowDriver is rarely reused due to the nature of the target, and there is only one for each target Window.
118 | This is the difference from Control Driver.
119 |
120 | Information on each window is required when implementing WindowDriver.
121 | Specifically, it is the information for identifying the Control such as the field name and the binding name in WPF.
122 | In WinForms, it is often easy to specify by field, but in WPF it is often that x:name is not attached, and in the case of Win32 it is not .net so the field cannot be used.
123 | For such cases, the library provides some specific methods for you to use.
124 | Even if this is not enough, if you understand the basics of Friendly, you can create a new identification method yourself.
125 | + [WPF](https://github.com/Roommetro/Friendly.WPFStandardControls/) 126 | + [Win32](https://github.com/Codeer-Software/Friendly.Windows.Grasp) 127 | 128 | The necessary knowledge is information about the implementation/design of the app from .Net.
129 | Create an extension method for Attach about WindowDriver of top-level Window.
130 | 131 | ```cs 132 | using Codeer.Friendly.Dynamic; 133 | using Codeer.Friendly.Windows; 134 | using Codeer.Friendly.Windows.Grasp; 135 | using RM.Friendly.WPFStandardControls; 136 | 137 | namespace Driver.Windows 138 | { 139 | public class MainWindowDriver 140 | { 141 | public WindowControl Core { get; } 142 | 143 | //get by field. 144 | public WPFTextBox Name => thid.Dynamic()._textBoxName; 145 | 146 | //get by type. 147 | public WPFMenuBase Menu => Core.LogicalTree().ByType("System.Windows.Controls.Menu").Single().Dynamic(); 148 | 149 | //get by binding. 150 | public WPFComboBox Preferredlanguage => Core.LogicalTree().ByBinding("LanguageSearch.Value").Single().Dynamic(); 151 | public WPFDataGrid DataGrid => Core.LogicalTree().ByBinding("SelectedItem.Value").Single().Dynamic(); 152 | 153 | //get by text. 154 | public WPFButtonBase Search => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Search").Single().Dynamic(); 155 | public WPFButtonBase Add => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Add").Single().Dynamic(); 156 | public WPFButtonBase Delete => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Delete").Single().Dynamic(); 157 | 158 | public MainWindowDriver(WindowControl core) => Core = core; 159 | } 160 | 161 | //provide Attach as an extension method 162 | public static class MainWindowDriverExtensions 163 | { 164 | public static MainWindowDriver AttachMainWindow(this WindowsAppFriend app) 165 | => new MainWindowDriver(app.WaitForIdentifyFromTypeFullName("DemoApp.Views.MainWindow")); 166 | } 167 | } 168 | ``` 169 | ```cs 170 | [TestMethod] 171 | public void Sample() 172 | { 173 | //attach 174 | var mainWindow = _app.AttachMainWindow(); 175 | 176 | //manipulation 177 | mainWindow.Name.EmulateChangeText("ishikawa"); 178 | ``` 179 | In the case of UserControl, there are two ways to get it.
180 | ```cs 181 | using Codeer.Friendly; 182 | using Codeer.Friendly.Dynamic; 183 | using RM.Friendly.WPFStandardControls; 184 | using Driver.CustomDrivers; 185 | using System.Windows.Controls; 186 | 187 | namespace Driver.Windows 188 | { 189 | public class EntryControlDriver 190 | { 191 | public WPFUserControl Core { get; } 192 | public WPFTextBox Name => Core.Dynamic()._textBoxName; 193 | public WPFContextMenu NameContextMenu => new WPFContextMenu{Target = Name.AppVar}; 194 | public WPFTextBox email => Core.LogicalTree().ByBinding("Mail.Value").Single().Dynamic(); 195 | public WPFContextMenu emailContextMenu => new WPFContextMenu{Target = email.AppVar}; 196 | public WPFComboBox Preferredlanguage => Core.LogicalTree().ByBinding("Language.Value").Single().Dynamic(); 197 | public WPFToggleButton Man => Core.LogicalTree().ByBinding("IsMan.Value").Single().Dynamic(); 198 | public WPFToggleButton Woman => Core.LogicalTree().ByBinding("IsWoman.Value").Single().Dynamic(); 199 | public WPFCalendar Birthday => Core.LogicalTree().ByBinding("BirthDay.Value").Single().Dynamic(); 200 | public WPFNumericUpDownDriver Age => Core.LogicalTree().ByBinding("Age.Value").Single().Dynamic(); 201 | public WPFButtonBase Entry => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Entry").Single().Dynamic(); 202 | public WPFButtonBase Cancel => Core.LogicalTree().ByType("System.Windows.Controls.Button").ByType().ByContentText("Cancel").Single().Dynamic(); 203 | 204 | public EntryControlDriver(AppVar core) 205 | => Core = new WPFUserControl(core); 206 | } 207 | } 208 | ``` 209 | The first is to use the property of the parent Window Driver.
210 | ```cs 211 | public class MainWindowDriver 212 | { 213 | public WindowControl Core { get; } 214 | 215 | //parent window's propery. 216 | public EntryControlDriver EntryControl => Core.Dynamic()._entryControl; 217 | 218 | public MainWindow_Driver(WindowControl core) => Core = core; 219 | } 220 | ``` 221 | The other is to create an extension method that attaches to WindowDriver.
222 | ```cs 223 | //get by extension method. 224 | public static class EntryControlDriverExtensions 225 | { 226 | public static EntryControlDriver AttachEntryControl(this MainWindowDriver window) 227 | => window.Core.VisualTree().ByType("DemoApp.Views.EntryControl").Single().Dynamic(); 228 | } 229 | ``` 230 | This is suitable for cases where MDI or WPF pages do not always exist or there are multiple windows/UserControls that are the same.
231 | In some cases, like docking windows, where you don't know which parent window it has,
232 | you may want to create an extension method that attaches to WindowsAppFriend even if it's not the top-level window.
233 | If you want to implement the process to be executed inside the target process at the time of manipulation, it is recommended to divide the process into another dll.
234 | This is for implementation efficiency. When loaded into the target process, the dll cannot be recompiled while the process is running.
235 | Since there is relatively little work to load in the target process, if you keep it separate, you can compile while the process is running.
236 | 237 | Ultimately, we recommend this Dll configuration.
238 | 239 | ![Dlls.png](Img/Dlls.png) 240 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/DynamicAppType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Dynamic; 3 | using Codeer.Friendly.Dynamic.Inside; 4 | using Codeer.Friendly.Dynamic.Properties; 5 | using System.Collections.Generic; 6 | 7 | namespace Codeer.Friendly.Dynamic 8 | { 9 | #if ENG 10 | /// 11 | /// Allows accessing type-specific operations in the target application. 12 | /// Allows using the dynamic keyword to access static FriendlyOperations in a class just as you would access ordinary methods, properties, and fields. 13 | /// Also allows creating objects in the target application by using their class name. 14 | /// One limitation is that it is not possible to call static methods with the same name as those defined in the DynamicObject class. 15 | /// If you absolutely need to be able to do so, please use the FriendlyOperation APIs. 16 | /// 17 | #else 18 | /// 19 | /// 対象アプリケーション内の型による操作に関するクラスです。 20 | /// static操作に対するFriendlyOperationをdynamicキーワードによって、 21 | /// 通常のメソッド、プロパティー、フィールド呼び出しのように扱うことができます。 22 | /// また、型名称を使って、対象アプリケーション内にオブジェクトを生成することもできます。 23 | /// 注意点として、DynamicObjectクラスに定義されている操作と同名のstatic操作を呼び出すことはできません。 24 | /// どうしても必要な場合は、旧来のFriendlyOperationで実行してください。 25 | /// 26 | /// 27 | /// ・System.Windows.Forms.ControlクラスのFromHandleを呼び出します。 28 | /// app.Type().System.Windows.Forms.Control.FromHandle(handle); 29 | /// 30 | /// ・System.Windows.Forms.Controlクラスを生成します。 31 | /// app.Type().System.Windows.Forms.Control(); 32 | /// 33 | #endif 34 | public class DynamicAppType : DynamicObject, IDefinition 35 | { 36 | const string TypeCacheKey = "Codeer.Friendly.Dynamic.TypeCache"; 37 | 38 | readonly AppFriend _app; 39 | readonly string _name; 40 | readonly bool _isType; 41 | 42 | #if ENG 43 | /// 44 | /// Constructor. 45 | /// 46 | /// Application manipulation object. 47 | #else 48 | /// 49 | /// コンストラクタ。 50 | /// 51 | /// アプリケーション操作クラス。 52 | #endif 53 | public DynamicAppType(AppFriend app) : this(app, string.Empty) { } 54 | 55 | #if ENG 56 | /// 57 | /// Constructor. 58 | /// 59 | /// Application manipulation object. 60 | /// Type full name. 61 | #else 62 | /// 63 | /// コンストラクタ。 64 | /// 65 | /// アプリケーション操作クラス。 66 | /// 型名称。 67 | #endif 68 | public DynamicAppType(AppFriend app, string name) 69 | { 70 | _app = app; 71 | _name = name; 72 | _isType = IsTypeName(_app, name, false); 73 | } 74 | 75 | #if ENG 76 | /// 77 | /// Dynamically resolves get access to properties and fields. 78 | /// Cannot be used asynchorously. 79 | /// If you need asynchronous access, please cast the object to an AppVar 80 | /// and use a FriendlyOperation, or call it in the form of a method by specifying a 81 | /// method name equal to the property name. 82 | /// 83 | /// Binder. 84 | /// Retrieved result. 85 | /// Success or failure. 86 | #else 87 | /// 88 | /// プロパティーへのアクセス(getter)の動的解決です。 89 | /// 非同期実行はできません。 90 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 91 | /// メソッド形式の呼び出しを実行してください。 92 | /// メソッド名称はプロパティー名称と同一です。 93 | /// 94 | /// バインダー。 95 | /// 取得結果。 96 | /// 成否。 97 | #endif 98 | public override bool TryGetMember(GetMemberBinder binder, out object result) 99 | { 100 | string nextName = JointName(_name, binder.Name); 101 | 102 | if (!_isType || //現在の名前が型でないのなら、つなげる 103 | IsTypeName(_app, nextName, _isType)) //現在の名前が型でも、つなげた名前が型になるなら、つなげる。 104 | { 105 | dynamic next = new DynamicAppType(_app, nextName); 106 | result = next; 107 | } 108 | else 109 | { 110 | //staticなgetter呼び出し。 111 | result = _app[nextName]().Dynamic(); 112 | } 113 | return true; 114 | } 115 | 116 | #if ENG 117 | /// 118 | /// Dynamically resolves set access to properties and fields. 119 | /// Cannot be used asynchronously. 120 | /// If you need asynchronous access, please cast the object to an AppVar 121 | /// and use a FriendlyOperation, or call it in the form of a method by specifying a 122 | /// method name equal to the property name. 123 | /// 124 | /// Binder. 125 | /// Value to set. 126 | /// Success or failure. 127 | #else 128 | /// 129 | /// プロパティーへのアクセス(setter)の動的解決です。 130 | /// 非同期実行はできません。 131 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 132 | /// メソッド形式の呼び出しを実行してください。 133 | /// メソッド名称はプロパティー名称と同一です。 134 | /// 135 | /// バインダー。 136 | /// 設定値。 137 | /// 成否。 138 | #endif 139 | public override bool TrySetMember(SetMemberBinder binder, object value) 140 | { 141 | _app[JointName(_name, binder.Name)](value); 142 | return true; 143 | } 144 | 145 | #if ENG 146 | /// 147 | /// Dynamically resolves method calls 148 | /// To specify Async and/or OperationTypeInfo, please pass these as arguments. 149 | /// They can be included in any order. 150 | /// 151 | /// Binder. 152 | /// Arguments. 153 | /// Return value. 154 | /// Success or failure. 155 | #else 156 | /// 157 | /// メソッド実行の動的解決です。 158 | /// AsyncとOperationTypeInfoを指定する場合は、引数に渡してください。 159 | /// その順番はどこでも構いません。 160 | /// 161 | /// バインダー。 162 | /// 引数。 163 | /// 戻り値。 164 | /// 成否。 165 | #endif 166 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 167 | { 168 | result = InvokeMember(binder.Name, args); 169 | return true; 170 | } 171 | 172 | #if ENG 173 | /// 174 | /// Dynamically resolves delegate types. 175 | /// When called on this class, calls the constructor in the target application and creates a class instance. 176 | /// 177 | /// Binder. 178 | /// Arguments. 179 | /// Return value. 180 | /// Success or failure. 181 | #else 182 | /// 183 | /// delegate型の実行の動的解決です。 184 | /// このクラスで呼ばれた場合、対象アプリケーション内で、その型のコンストラクタを呼び出して、インスタンスを生成します。 185 | /// 186 | /// バインダー。 187 | /// 引数。 188 | /// 戻り値。 189 | /// 成否。 190 | #endif 191 | public override bool TryInvoke(InvokeBinder binder, object[] args, out object result) 192 | { 193 | result = NewAppVar(_app, _name, args); 194 | return true; 195 | } 196 | 197 | #if ENG 198 | /// 199 | /// Calls the static Equals() method, if one is defined. 200 | /// 201 | /// Object. 202 | /// Returned as a DynamicAppVar. 203 | #else 204 | /// 205 | /// Equalsという名前のstaticメソッドがあれば、それを実行します。 206 | /// 207 | /// オブジェクト。 208 | /// 戻り値。DynamicAppVarで返ります。 209 | #endif 210 | public new dynamic Equals(object obj) 211 | { 212 | return InvokeMember("Equals", new object[] { obj }); 213 | } 214 | 215 | #if ENG 216 | /// 217 | /// Calls the static GetHashCode() method, if it is defined. 218 | /// 219 | /// Returned as a DynamicAppVar. 220 | #else 221 | /// 222 | /// GetHashCodeという名前のstaticメソッドがあれば、それを実行します。 223 | /// 224 | /// 戻り値。DynamicAppVarで返ります。 225 | #endif 226 | public new dynamic GetHashCode() 227 | { 228 | return InvokeMember("GetHashCode", new object[0]); 229 | } 230 | 231 | #if ENG 232 | /// 233 | /// Calls the static ToString() method, if it is defined. 234 | /// 235 | /// Returned as a DynamicAppVar. 236 | #else 237 | /// 238 | /// ToStringという名前のstaticメソッドがあれば、それを実行します。 239 | /// 240 | /// 戻り値。DynamicAppVarで返ります。 241 | #endif 242 | public new dynamic ToString() 243 | { 244 | return InvokeMember("ToString", new object[0]); 245 | } 246 | 247 | #if ENG 248 | /// 249 | /// Calls the static GetType() method, if it is defined. 250 | /// 251 | /// Type object returned as a DynamicAppVar. 252 | #else 253 | /// 254 | /// GetTypeという名前のstaticメソッドがあれば、それを実行します。 255 | /// 256 | /// タイプ。DynamicAppVarで返ります。 257 | #endif 258 | public new dynamic GetType() 259 | { 260 | return InvokeMember("GetType", new object[0]); 261 | } 262 | 263 | #if ENG 264 | /// 265 | /// Calls the static MemberwiseClone() method, if it is defined. 266 | /// 267 | /// Returned as a DynamicAppVar. 268 | #else 269 | /// 270 | /// MemberwiseCloneという名前のstaticメソッドがあれば、それを実行します。 271 | /// 272 | /// 戻り値。DynamicAppVarで返ります。 273 | #endif 274 | public new dynamic MemberwiseClone() 275 | { 276 | return InvokeMember("MemberwiseClone", new object[0]); 277 | } 278 | 279 | /// 280 | /// メソッド実行の動的解決です。 281 | /// AsyncとOperationTypeInfoを指定する場合は、引数に渡してください。 282 | /// その順番はどこでも構いません。 283 | /// 284 | /// 名前。 285 | /// 引数。 286 | /// 戻り値。 287 | private dynamic InvokeMember(string name, object[] args) 288 | { 289 | string nextTypeName = JointName(_name, name); 290 | if (IsTypeName(_app, nextTypeName, _isType)) 291 | { 292 | //型名称になれば、生成する。 293 | return NewAppVar(_app, nextTypeName, args); 294 | } 295 | else 296 | { 297 | //staticメソッドの呼び出し。 298 | Async async; 299 | OperationTypeInfo typeInfo; 300 | args = DynamicFriendlyOperationUtility.ResolveArguments(args, out async, out typeInfo); 301 | return DynamicFriendlyOperationUtility.GetFriendlyOperation(_app, nextTypeName, async, typeInfo)(args).Dynamic(); 302 | } 303 | } 304 | 305 | /// 306 | /// 対象アプリケーション内にインスタンス生成。 307 | /// 308 | /// アプリケーション操作クラス。 309 | /// 生成する型のフルネーム。 310 | /// 引数。 311 | /// 生成したインスタンスを操作するためのDynamicAppVar。 312 | private static dynamic NewAppVar(AppFriend app, string typeFullName, object[] args) 313 | { 314 | Async async; 315 | OperationTypeInfo typeInfo; 316 | args = DynamicFriendlyOperationUtility.ResolveArguments(args, out async, out typeInfo); 317 | if (async != null) 318 | { 319 | throw new FriendlyOperationException(Resources.ErrorInstanceCreateCantUseAsync); 320 | } 321 | return (typeInfo == null) ? app.Dim(new NewInfo(typeFullName, args)).Dynamic() : 322 | app.Dim(new NewInfo(typeFullName, args), typeInfo).Dynamic(); 323 | } 324 | 325 | /// 326 | /// 名前の結合。 327 | /// 328 | /// 名前1。 329 | /// 名前2。 330 | /// 結合された名前。 331 | private static string JointName(string name1, string name2) 332 | { 333 | return string.IsNullOrEmpty(name1) ? name2 : name1 + "." + name2; 334 | } 335 | 336 | /// 337 | /// タイプの名前であるか。 338 | /// 339 | /// アプリケーション操作クラス。 340 | /// 名前。 341 | /// タイプでなくてもキャッシュする。 342 | /// タイプの名前であるか。 343 | private static bool IsTypeName(AppFriend app, string name, bool cacheNotType) 344 | { 345 | if (string.IsNullOrEmpty(name)) 346 | { 347 | return false; 348 | } 349 | 350 | //初回登録時にマルチスレッドでアクセスされた場合 351 | //初回のキャッシュ登録が無駄になるが、許容する 352 | object typeCache; 353 | if (!app.TryGetAppControlInfo(TypeCacheKey, out typeCache)) 354 | { 355 | typeCache = new TypeChahe(app); 356 | app.AddAppControlInfo(TypeCacheKey, typeCache); 357 | } 358 | lock (typeCache) 359 | { 360 | return ((TypeChahe)typeCache).IsTypeName(app, name, cacheNotType); 361 | } 362 | } 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly/AppVar.cs: -------------------------------------------------------------------------------- 1 | #define CODE_ANALYSIS 2 | using System; 3 | using Codeer.Friendly.Inside; 4 | using Codeer.Friendly.Inside.Protocol; 5 | using System.Diagnostics.CodeAnalysis; 6 | using Codeer.Friendly.Properties; 7 | 8 | namespace Codeer.Friendly 9 | { 10 | #if ENG 11 | /// 12 | /// Represents variables in the target application. 13 | /// Operates properies, fields, methods. 14 | /// Can acquire variable values from the test process, and set values to the test process. 15 | /// When the variables in application support iterative processes (In case of .Net, it inherits IEnumerator), 16 | /// can use foreach to iterate through them. 17 | /// As long as an instance of this class remains in the test process, the underlying object in the target application will not be marked for garbage collection. 18 | /// Timing of the garbage collection within the test process is not affected by the memory state of the product process. 19 | /// If needed, use methods such as System.GC's Collect() or WaitForPendingFinalizers() to trigger garbage collection and delete instances from the test process. 20 | /// You can also use Dispose() to release a resource immediately. 21 | /// 22 | #else 23 | /// 24 | /// アプリケーション内部の変数クラスです。 25 | /// プロパティー、フィールド、メソッドの操作ができます。 26 | /// また、変数の中の値をテストプロセス側へ取得したりテストプロセス側の値を設定したりもできます。 27 | /// さらに、アプリケーション内の変数が繰り返し処理に対応している場合(.NetならIEnumeratorを継承している場合)foreachが使用できます。 28 | /// このクラスのインスタンスがテストプロセスに残っている限り、対象プロセス内部のインスタンスもガベージコレクションの対象になりません。 29 | /// テストプロセスのガベージコレクション実施のタイミングはプロダクトプロセスのメモリ状態とは無関係です。 30 | /// 必要があれば、System.GCクラスのCollectメソッドやWaitForPendingFinalizersのような 31 | /// ガベージコレクションを促進するメソッドを使用してテストプロセスからこのインスタンスを削除してください。 32 | /// また、直ちに解放したい場合はDisposeメソッドを使用してください。 33 | /// 34 | #endif 35 | public class AppVar : OperationTalker, IDisposable 36 | { 37 | readonly VarAddress _varAddress; 38 | readonly IFriendlyConnector _friendlyConnector; 39 | bool _disposed; 40 | 41 | /// 42 | /// 変数アドレス。 43 | /// 44 | internal VarAddress VarAddress { get { return _varAddress; } } 45 | 46 | /// 47 | /// アプリケーションとの接続クラス。 48 | /// 49 | internal override IFriendlyConnector FriendlyConnector { get { return _friendlyConnector; } } 50 | 51 | #if ENG 52 | /// 53 | /// Returns the associated application manipulation object. 54 | /// 55 | #else 56 | /// 57 | /// アプリケーション操作クラスを取得します。 58 | /// 59 | #endif 60 | public AppFriend App { get { return _friendlyConnector.App; } } 61 | 62 | #if ENG 63 | /// 64 | /// Get that variables in the target application is null. 65 | /// 66 | #else 67 | /// 68 | /// アプリケーション内変数がnullであるかを取得します。 69 | /// 70 | #endif 71 | public bool IsNull { get { return (bool)SendAndValueReceive(ProtocolType.IsEmptyVar, null, string.Empty, new object[] { this }); } } 72 | 73 | /// 74 | /// コンストラクタ。 75 | /// 76 | /// アプリケーションとの接続クラス。 77 | /// 変数アドレス 78 | internal AppVar(IFriendlyConnector friendlyConnector, VarAddress varAddress) 79 | { 80 | _friendlyConnector = friendlyConnector; 81 | _varAddress = varAddress; 82 | } 83 | 84 | /// 85 | /// ファイナライザ。 86 | /// 87 | ~AppVar() 88 | { 89 | Dispose(false); 90 | } 91 | 92 | #if ENG 93 | /// 94 | /// Releases the application's variable from management. 95 | /// After calling this method, the variable of this AppVar cannot be accessed. 96 | /// In many cases, it is not necessary to call this explicitly. 97 | /// If needed, please use methods such as System.GC's Collect() or WaitForPendingFinalizers() 98 | /// to trigger garbage collection. 99 | /// Please use this, for example, when the variable inside the application uses a lot of memory. 100 | /// 101 | #else 102 | /// 103 | /// アプリケーション内部の変数を管理から削除します。 104 | /// このメソッドを呼び出した後、そのAppVarの変数にはアクセスできません。 105 | /// このメソッドはファイナライザからも実施されます。 106 | /// 多くの場合、これを明示的に呼び出す必要はありません。 107 | /// 必要があれば、System.GCクラスのCollectメソッドやWaitForPendingFinalizersのような 108 | /// ガベージコレクションを促進するメソッドを利用してください。 109 | /// アプリケーション内部の変数が特別に大きなメモリの場合など、特殊な場合に利用してください。 110 | /// 111 | #endif 112 | public void Dispose() 113 | { 114 | Dispose(true); 115 | GC.SuppressFinalize(this); 116 | } 117 | 118 | #if ENG 119 | /// 120 | /// Despose. 121 | /// 122 | /// flag. 123 | #else 124 | /// 125 | /// 破棄。 126 | /// 127 | /// 破棄フラグ。 128 | #endif 129 | [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] 130 | protected virtual void Dispose(bool disposing) 131 | { 132 | if (_disposed) 133 | { 134 | return; 135 | } 136 | 137 | //ファイナライザのタイミングによっては相手アプリケーションが存在しないことは十分にありうる。 138 | try 139 | { 140 | SendAndReceive(ProtocolType.BinOff, null, string.Empty, new object[0]); 141 | } 142 | catch { } 143 | _disposed = true; 144 | } 145 | 146 | #if ENG 147 | /// 148 | /// Acquires a delegate which can call operations on variables in the test target application. 149 | /// 150 | /// Operation name. 151 | /// Delegate for executing operations. 152 | #else 153 | /// 154 | /// テスト対象アプリケーション内の変数の操作を呼び出すdelegateを取得します。 155 | /// 156 | /// 操作名称。 157 | /// 操作実行delegate。 158 | #endif 159 | public FriendlyOperation this[string operation] 160 | { 161 | get 162 | { 163 | return (new FriendlyOperationOwner(this, null, operation)).FriendlyOperation; 164 | } 165 | } 166 | 167 | #if ENG 168 | /// 169 | /// Acquires a delegate which can call operations on variables in the test target application. 170 | /// 171 | /// Operation name. 172 | /// Object for asynchronous execution. 173 | /// Delegate for executing operations. 174 | #else 175 | /// 176 | /// テスト対象アプリケーション内の変数の操作を呼び出すdelegateを取得します。 177 | /// 178 | /// 操作名称。 179 | /// 非同期実行オブジェクト。 180 | /// 操作実行delegate。 181 | #endif 182 | public FriendlyOperation this[string operation, Async async] 183 | { 184 | get 185 | { 186 | if (async == null) 187 | { 188 | throw new ArgumentNullException("async"); 189 | } 190 | async.Initialize(this); 191 | return (new AsyncFriendlyOperationOwner(async, null, operation)).FriendlyOperation; 192 | } 193 | } 194 | 195 | #if ENG 196 | /// 197 | /// Acquires a delegate which can call operations on variables in the test target application. 198 | /// 199 | /// Operation name. 200 | /// 201 | /// Operation type information. 202 | /// Used to differentiate between multiple overloads or to target an operation with the same name within a parent class. 203 | /// Overloads can often be resolved by their parameters without specifying an OperationTypeInfo. 204 | /// 205 | /// Delegate for executing operations. 206 | #else 207 | /// 208 | /// テスト対象アプリケーション内の変数の操作を呼び出すdelegateを取得します。 209 | /// 210 | /// 操作名称。 211 | /// 操作タイプ情報。(オーバーロードの解決等に使用します。) 212 | /// 操作実行delegate。 213 | #endif 214 | public FriendlyOperation this[string operation, OperationTypeInfo operationTypeInfo] 215 | { 216 | get 217 | { 218 | return (new FriendlyOperationOwner(this, operationTypeInfo, operation)).FriendlyOperation; 219 | } 220 | } 221 | 222 | #if ENG 223 | /// 224 | /// Acquires a delegate which can call operations on variables in the test target application. 225 | /// 226 | /// Operation name. 227 | /// 228 | /// Operation type information. 229 | /// Used to differentiate between multiple overloads or to target an operation with the same name within a parent class. 230 | /// Overloads can often be resolved by their parameters without specifying an OperationTypeInfo. 231 | /// 232 | /// Object for asynchronous execution. 233 | /// Delegate for executing operations. 234 | #else 235 | /// 236 | /// テスト対象アプリケーション内の変数の操作を呼び出すdelegateを取得します。 237 | /// 238 | /// 操作名称。 239 | /// 操作タイプ情報。(オーバーロードの解決等に使用します。) 240 | /// 非同期実行オブジェクト。 241 | /// 操作実行delegate。 242 | #endif 243 | public FriendlyOperation this[string operation, OperationTypeInfo operationTypeInfo, Async async] 244 | { 245 | get 246 | { 247 | if (async == null) 248 | { 249 | throw new ArgumentNullException("async"); 250 | } 251 | async.Initialize(this); 252 | return (new AsyncFriendlyOperationOwner(async, operationTypeInfo, operation)).FriendlyOperation; 253 | } 254 | } 255 | 256 | #if ENG 257 | /// 258 | /// Serializes a variable and passes it to the application under test or sends a value to it. 259 | /// Can only be used when the object can be serialized. 260 | /// AppVar can also be assigned to the setter. 261 | /// 262 | #else 263 | /// 264 | /// 変数をシリアライズして、アプリケーションからテストプロセスへ取得もしくは、 265 | /// テストプロセスからアプリケーションへ設定します。 266 | /// このプロパティー指定するオブジェクトがシリアライズ可能な場合のみ使用可能です。 267 | /// setterにはAppVarも指定可能です。 268 | /// 269 | #endif 270 | public object Core 271 | { 272 | get 273 | { 274 | return SendAndValueReceive(ProtocolType.GetValue, null, string.Empty, new object[0]); 275 | } 276 | set 277 | { 278 | SendAndReceive(ProtocolType.SetValue, null, string.Empty, new object[] { value }); 279 | } 280 | } 281 | 282 | #if ENG 283 | /// 284 | /// Equivalence comparison. 285 | /// Carries out an equivalence comparison against the variable in the application. 286 | /// 287 | /// 288 | /// A candidate for the comparison. 289 | /// AppVar can also be specified. 290 | /// Please refer to sample code. 291 | /// 292 | /// true is returned when the values are equal. 293 | #else 294 | /// 295 | /// 等価比較。 296 | /// 操作対象アプリケーション内部で実施した結果を返します。 297 | /// 298 | /// オブジェクト。 299 | /// 比較結果。 300 | #endif 301 | public override bool Equals(object obj) 302 | { 303 | return (bool)this["Equals"](obj).Core; 304 | } 305 | 306 | #if ENG 307 | /// 308 | /// Acquires a hash code of the variable in the application. 309 | /// 310 | /// Hash code of the variable in the application. 311 | #else 312 | /// 313 | /// ハッシュコード取得。 314 | /// 操作対象アプリケーション内部で実施した結果を返します。 315 | /// 316 | /// ハッシュコード。 317 | #endif 318 | public override int GetHashCode() 319 | { 320 | return (int)this["GetHashCode"]().Core; 321 | } 322 | 323 | #if ENG 324 | /// 325 | /// Produces a string value for the object in the application. 326 | /// 327 | /// String representing the object in the application. 328 | #else 329 | /// 330 | /// 文字列変換。 331 | /// 操作対象アプリケーション内部で実施した結果を返します。 332 | /// 333 | /// 文字列。 334 | #endif 335 | public override string ToString() 336 | { 337 | return (string)this["ToString"]().Core; 338 | } 339 | 340 | /// 341 | /// 戻り値をAppVarで取得する通信。 342 | /// 343 | /// 通信タイプ。 344 | /// 操作タイプ情報。 345 | /// 操作名称。 346 | /// 引数。 347 | /// 変数。 348 | internal override AppVar SendAndReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments) 349 | { 350 | if (_disposed) 351 | { 352 | throw new FriendlyOperationException(Resources.ErrorDisposedObject); 353 | } 354 | return FriendlyTalker.SendAndVarReceive(this, _friendlyConnector, protocolType, operationTypeInfo, _varAddress, string.Empty, operation, arguments); 355 | } 356 | 357 | /// 358 | /// 戻り値を値で取得する通信処理。 359 | /// 360 | /// 通信タイプ。 361 | /// 操作タイプ情報。 362 | /// 操作名称。 363 | /// 引数。 364 | /// 値。 365 | internal override object SendAndValueReceive(ProtocolType protocolType, OperationTypeInfo operationTypeInfo, string operation, object[] arguments) 366 | { 367 | if (_disposed) 368 | { 369 | throw new FriendlyOperationException(Resources.ErrorDisposedObject); 370 | } 371 | return FriendlyTalker.SendAndValueReceive(this, _friendlyConnector, protocolType, operationTypeInfo, _varAddress, string.Empty, operation, arguments); 372 | } 373 | } 374 | } 375 | -------------------------------------------------------------------------------- /Project/Codeer.Friendly.Dynamic/DynamicAppVar.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Dynamic; 4 | using Codeer.Friendly.Dynamic.Inside; 5 | using System.Runtime.Serialization; 6 | using Codeer.Friendly.Dynamic.Properties; 7 | using System.Collections; 8 | using System.Text; 9 | 10 | namespace Codeer.Friendly.Dynamic 11 | { 12 | #if ENG 13 | /// 14 | /// Wrapper class to allow using the dynamic keyword to access objects' 15 | /// methods, properties and fields just like ordinary class members. 16 | /// One limitation is that you it is not possible to access members that have the same name as those 17 | /// defined in the DynamicObject class. 18 | /// And it is not possible to access member the name CodeerFriendlyAppVar. 19 | /// In the case that you need to access such members, please use the FriendlyObject APIs. 20 | /// 21 | #else 22 | /// 23 | /// AppVarのFriendlyOperationをdynamicキーワードを使うことにより、 24 | /// 通常のメソッド、プロパティー、フィールド呼び出しのように扱うためのラッパークラスです。 25 | /// 注意点として、DynamicObjectクラスに定義されている操作と同名の操作を呼び出すことはできません。 26 | /// またCodeerFriendlyAppVarという名称の操作も呼び出すことはできません。 27 | /// どうしても必要な場合は、旧来のFriendlyOperationで実行してください。 28 | /// 29 | #endif 30 | public class DynamicAppVar : DynamicObject, IAppVarSelf 31 | { 32 | readonly AppVar _appVar; 33 | 34 | #if ENG 35 | /// 36 | /// AppVar to wrap. 37 | /// 38 | #else 39 | /// 40 | /// 内部的に保持する対象アプリケーション内変数。 41 | /// 42 | #endif 43 | public AppVar CodeerFriendlyAppVar { get { return _appVar; } } 44 | 45 | #if ENG 46 | /// 47 | /// Constructor. 48 | /// 49 | /// AppVar to wrap. 50 | #else 51 | /// 52 | /// コンストラクタ。 53 | /// 54 | /// ラップするappVar。 55 | #endif 56 | public DynamicAppVar(AppVar appVar) 57 | { 58 | _appVar = appVar; 59 | } 60 | 61 | #if ENG 62 | /// 63 | /// Casts a DynamicAppVar to an AppVar. 64 | /// 65 | /// DynamicAppVar. 66 | /// AppVar. 67 | #else 68 | /// 69 | /// AppVarにキャストします。 70 | /// 71 | /// DynamicAppVar型変数。 72 | /// AppVar型。 73 | #endif 74 | public static implicit operator AppVar(DynamicAppVar src) 75 | { 76 | return src._appVar; 77 | } 78 | 79 | #if ENG 80 | /// 81 | /// Casts an AppVar to a DynamicAppVar. 82 | /// 83 | /// AppVar. 84 | /// DynamicAppVar. 85 | #else 86 | /// 87 | /// DynamicAppVarにキャストします。 88 | /// 89 | /// AppVar型変数。 90 | /// DynamicAppVar型。 91 | #endif 92 | public static implicit operator DynamicAppVar(AppVar src) 93 | { 94 | return new DynamicAppVar(src); 95 | } 96 | 97 | #if ENG 98 | /// 99 | /// Allows dynamic resolution for other types of casts. 100 | /// Serializes the value from the target application and retrieves it. 101 | /// Special cases 102 | /// • When casting to IDisposable, an internal AppVar is returned. 103 | /// · When casting to an IEnumerable, an IEnumerable<dynamic> is returned. The dynamic object in this case is a DynamicAppVar. 104 | /// 105 | /// Binder. 106 | /// Cast result. 107 | /// Success or failure. 108 | #else 109 | /// 110 | /// その他のキャストの動的解決です。 111 | /// 対象アプリケーションから、値をシリアライズして取得します。 112 | /// 特殊処理として、以下のものがあります。 113 | /// ・IDisposableにキャストすると、内部のAppVarが返ります。 114 | /// ・IEnumerableにキャストするとIEnumerable<dynamic>が返ります。dynamicはDynamicAppVarです。 115 | /// 116 | /// バインダー。 117 | /// キャスト結果。 118 | /// 成否。 119 | #endif 120 | public override bool TryConvert(ConvertBinder binder, out object result) 121 | { 122 | if (typeof(IDisposable) == binder.Type) 123 | { 124 | result = _appVar; 125 | } 126 | else if (typeof(IEnumerable) == binder.Type) 127 | { 128 | result = new Enumerate(_appVar).Select(e => e.Dynamic()); 129 | } 130 | else 131 | { 132 | if (!binder.Type.IsSerializable && binder.Type.GetConstructor(new[] { typeof(AppVar) }) != null) 133 | { 134 | //AppVarのみを渡すコンストラクタを持っている場合 135 | if (_appVar.IsNull) 136 | { 137 | result = null; 138 | } 139 | else 140 | { 141 | result = Activator.CreateInstance(binder.Type, new object[] { _appVar }); 142 | } 143 | } 144 | else 145 | { 146 | result = _appVar.Core; 147 | } 148 | } 149 | return true; 150 | } 151 | 152 | #if ENG 153 | /// 154 | /// Provides dynamic resolution of get indexes. 155 | /// Cannot be used asynchronously. 156 | /// If you need asynchronous access, please cast the object to an AppVar and use 157 | /// a FriendlyOperation, or access the index via its method name (.get_Item() for 158 | /// object indexers and .Get() for array indices). 159 | /// 160 | /// Binder. 161 | /// Index access arguments. 162 | /// Result. 163 | /// Success or failure. 164 | #else 165 | /// 166 | /// インデックスアクセス(getter)の動的解決です。 167 | /// 非同期実行はできません。 168 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 169 | /// メソッド形式の呼び出しを実行してください。 170 | /// メソッド名称はインデックスアクセスの別名(配列はGet,オブジェクトでの定義はget_Item)を使用してください。 171 | /// 172 | /// 173 | /// //非同期サンプル。 174 | /// dynamic array; //int[]のオブジェクトが格納されているとします。 175 | /// Async async = new Async(); 176 | /// int value = array.Get(async, 1); //メソッド形式で呼び出すことが出来ます。Asyncオブジェクトを指定することが出来ます。 177 | /// 178 | /// バインダー。 179 | /// インデックスアクセス引数。 180 | /// 取得。結果 181 | /// 成否。 182 | #endif 183 | public override bool TryGetIndex(GetIndexBinder binder, object[] indexes, out object result) 184 | { 185 | result = _appVar[GetIndexAccessOperation(indexes.Length)](indexes).Dynamic(); 186 | return true; 187 | } 188 | 189 | #if ENG 190 | /// 191 | /// Provides dynamic resolution of set indexes. 192 | /// Cannot be used asynchronously. 193 | /// By casting AppVar, you can use the FriendlyOperation If you need asynchronous execution by all means, 194 | /// Please execute the method call format. 195 | /// Please use the (set_Item definition Set, in the object array aliases) index access method name. 196 | /// 197 | /// Binder. 198 | /// Index access argument. 199 | /// Value to be set. 200 | /// Success or failure. 201 | #else 202 | /// 203 | /// インデックスアクセス(setter)の動的解決です。 204 | /// 非同期実行はできません。 205 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 206 | /// メソッド形式の呼び出しを実行してください。 207 | /// メソッド名称はインデックスアクセスの別名(配列はSet,オブジェクトでの定義はset_Item)を使用してください。 208 | /// 209 | /// 210 | /// //非同期サンプル。 211 | /// dynamic array; //int[]のオブジェクトが格納されているとします。 212 | /// Async async = new Async(); 213 | /// array.Set(async, 1, 100); //メソッド形式で呼び出すことが出来ます。Asyncオブジェクトを指定することが出来ます。 214 | /// 215 | /// バインダー。 216 | /// インデックスアクセス引数 217 | /// 設定する値 218 | /// 成否。 219 | #endif 220 | public override bool TrySetIndex(SetIndexBinder binder, object[] indexes, object value) 221 | { 222 | object[] args = DynamicFriendlyOperationUtility.AddSetterValue(indexes, value); 223 | _appVar[GetIndexAccessOperation(indexes.Length)](args); 224 | return true; 225 | } 226 | 227 | #if ENG 228 | /// 229 | /// Provides dynamic resolution of properties and fields. 230 | /// Cannot be used asynchronously. 231 | /// If you need asynchronous access, please cast the object to an AppVar 232 | /// and use a FriendlyOperation, or call it in the form of a method by specifying a 233 | /// method name equal to the property name. 234 | /// 235 | /// Binder. 236 | /// Return value. 237 | /// Success or failure. 238 | #else 239 | /// 240 | /// プロパティーへのアクセス(getter)の動的解決です。 241 | /// 非同期実行はできません。 242 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 243 | /// メソッド形式の呼び出しを実行してください。 244 | /// メソッド名称はプロパティー名称と同一です。 245 | /// 246 | /// 247 | /// dynamic form; //System.Windows.Forms.Formのオブジェクトが格納されているとします。 248 | /// Async async = new Async(); 249 | /// dynamic text = form.Text(async); //メソッド形式で呼び出すことが出来ます。Asyncオブジェクトを指定することが出来ます。 250 | /// 251 | /// バインダー。 252 | /// 取得結果。 253 | /// 成否。 254 | #endif 255 | public override bool TryGetMember(GetMemberBinder binder, out object result) 256 | { 257 | result = _appVar[binder.Name]().Dynamic(); 258 | return true; 259 | } 260 | 261 | #if ENG 262 | /// 263 | /// Provides dynamic resolution of properties and fields. 264 | /// Cannot be used asynchronously. 265 | /// If you need asynchronous access, please cast the object to an AppVar 266 | /// and use a FriendlyOperation, or call it in the form of a method by specifying a 267 | /// method name equal to the property name. 268 | /// 269 | /// Binder. 270 | /// Value to set. 271 | /// Success or failure. 272 | #else 273 | /// 274 | /// プロパティーへのアクセス(setter)の動的解決です。 275 | /// 非同期実行はできません。 276 | /// どうしても非同期実行が必要な場合はAppVarにキャストして、FriendlyOperationを使用するか、 277 | /// メソッド形式の呼び出しを実行してください。 278 | /// メソッド名称はプロパティー名称と同一です。 279 | /// 280 | /// 281 | /// dynamic form; //System.Windows.Forms.Formのオブジェクトが格納されているとします。 282 | /// Async async = new Async(); 283 | /// form.Text(async, "text"); //メソッド形式で呼び出すことが出来ます。Asyncオブジェクトを指定することが出来ます。 284 | /// 285 | /// バインダー。 286 | /// 設定値。 287 | /// 成否。 288 | #endif 289 | public override bool TrySetMember(SetMemberBinder binder, object value) 290 | { 291 | _appVar[binder.Name](value); 292 | return true; 293 | } 294 | 295 | #if ENG 296 | /// 297 | /// Provides dynamic resolution of method calls. 298 | /// To specify Async and/or OperationTypeInfo, please pass these as arguments. 299 | /// They can be included in any order. 300 | /// 301 | /// Binder. 302 | /// Arguments. 303 | /// Return value. 304 | /// Success or failure. 305 | #else 306 | /// 307 | /// メソッド実行の自動解決です。 308 | /// AsyncとOperationTypeInfoを指定する場合は、引数に渡してください。 309 | /// その順番はどこでも構いません。 310 | /// 311 | /// バインダー。 312 | /// 引数。 313 | /// 戻り値。 314 | /// 成否。 315 | #endif 316 | public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result) 317 | { 318 | Async async; 319 | OperationTypeInfo typeInfo; 320 | args = DynamicFriendlyOperationUtility.ResolveArguments(args, out async, out typeInfo); 321 | result = DynamicFriendlyOperationUtility.GetFriendlyOperation(_appVar, binder.Name, async, typeInfo)(args).Dynamic(); 322 | return true; 323 | } 324 | 325 | #if ENG 326 | /// 327 | /// Equality. Returns the result of carrying out the equality operation inside the target application. 328 | /// 329 | /// Object to compare. 330 | /// Comparison result. 331 | #else 332 | /// 333 | /// 等価比較。 操作対象アプリケーション内部で実施した結果を返します。 334 | /// 335 | /// オブジェクト。 336 | /// 比較結果。 337 | #endif 338 | public override bool Equals(object obj) 339 | { 340 | return _appVar.Equals(obj); 341 | } 342 | 343 | #if ENG 344 | /// 345 | /// Hash code access. Returns the result of the GetHashCode() operation within the target application. 346 | /// 347 | /// Hash code. 348 | #else 349 | /// 350 | /// ハッシュコード取得。 操作対象アプリケーション内部で実施した結果を返します。 351 | /// 352 | /// ハッシュコード。 353 | #endif 354 | public override int GetHashCode() 355 | { 356 | return _appVar.GetHashCode(); 357 | } 358 | 359 | #if ENG 360 | /// 361 | /// String conversion. Returns the result of the ToString() operation carried out within the target application. 362 | /// 363 | /// String. 364 | #else 365 | /// 366 | /// 文字列変換。 操作対象アプリケーション内部で実施した結果を返します。 367 | /// 368 | /// 文字列。 369 | #endif 370 | public override string ToString() 371 | { 372 | return _appVar.ToString(); 373 | } 374 | 375 | #if ENG 376 | /// 377 | /// Type retrieval. Returns the result of the GetType() operation carried out within the target application. 378 | /// 379 | /// Type object wrapped in a DynamicAppVar. 380 | #else 381 | /// 382 | /// タイプを取得。操作対象アプリケーション内部で実施した結果を返します。 383 | /// 384 | /// タイプ。DynamicAppVarで返ります。 385 | #endif 386 | public new dynamic GetType() 387 | { 388 | return _appVar["GetType"]().Dynamic(); 389 | } 390 | 391 | #if ENG 392 | /// 393 | /// Creates a shallow copy. Returns the result of the MemberwiseClone() carried out within the target application. 394 | /// 395 | /// Shallow copy wrapped in a DynamicAppVar. 396 | #else 397 | /// 398 | /// 簡易コピーの作成。操作対象アプリケーション内部で実施した結果を返します。 399 | /// 400 | /// 簡易コピー。DynamicAppVarで返ります。 401 | #endif 402 | public new dynamic MemberwiseClone() 403 | { 404 | return _appVar["MemberwiseClone"]().Dynamic(); 405 | } 406 | 407 | /// 408 | /// インデックス操作文字列取得。 409 | /// 410 | /// インデックス数 411 | /// インデックス操作文字列。 412 | private static string GetIndexAccessOperation(int indexCount) 413 | { 414 | StringBuilder builder = new StringBuilder(); 415 | builder.Append("["); 416 | for (int i = 0; i < indexCount - 1; i++) 417 | { 418 | builder.Append(","); 419 | } 420 | builder.Append("]"); 421 | return builder.ToString(); 422 | } 423 | } 424 | } 425 | --------------------------------------------------------------------------------