using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime;
using System.Text;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using ComTypes = System.Runtime.InteropServices.ComTypes;
 
 
// Invoke a Win32 P/Invoke call.
// Based on work by Lee Holmes
// http://www.leeholmes.com/blog/2006/07/21/get-the-owner-of-a-process-in-powershell-pinvoke-and-refout-parameters

/*
Author: Casey Smith, Twitter: @subTee
License: BSD 3-Clause
Create Your Strong Name Key -> key.snk
$key = 'BwIAAAAkAABSU0EyAAQAAAEAAQBhXtvkSeH85E31z64cAX+X2PWGc6DHP9VaoD13CljtYau9SesUzKVLJdHphY5ppg5clHIGaL7nZbp6qukLH0lLEq/vW979GWzVAgSZaGVCFpuk6p1y69cSr3STlzljJrY76JIjeS4+RhbdWHp99y8QhwRllOC0qu/WxZaffHS2te/PKzIiTuFfcP46qxQoLR8s3QZhAJBnn9TGJkbix8MTgEt7hD1DC2hXv7dKaC531ZWqGXB54OnuvFbD5P2t+vyvZuHNmAy3pX0BDXqwEfoZZ+hiIk1YUDSNOE79zwnpVP1+BN0PK5QCPCS+6zujfRlQpJ+nfHLLicweJ9uT7OG3g/P+JpXGN0/+Hitolufo7Ucjh+WvZAU//dzrGny5stQtTmLxdhZbOsNDJpsqnzwEUfL5+o8OhujBHDm/ZQ0361mVsSVWrmgDPKHGGRx+7FbdgpBEq3m15/4zzg343V9NBwt1+qZU+TSVPU0wRvkWiZRerjmDdehJIboWsx4V8aiWx8FPPngEmNz89tBAQ8zbIrJFfmtYnj1fFmkNu3lglOefcacyYEHPX/tqcBuBIg/cpcDHps/6SGCCciX3tufnEeDMAQjmLku8X4zHcgJx6FpVK7qeEuvyV0OGKvNor9b/WKQHIHjkzG+z6nWHMoMYV5VMTZ0jLM5aZQ6ypwmFZaNmtL6KDzKv8L1YN2TkKjXEoWulXNliBpelsSJyuICplrCTPGGSxPGihT3rpZ9tbLZUefrFnLNiHfVjNi53Yg4='
$Content = [System.Convert]::FromBase64String($key)
Set-Content key.snk -Value $Content -Encoding Byte
C:\Windows\Microsoft.NET\Framework\v2.0.50727\csc.exe /r:System.EnterpriseServices.dll /target:library /out:DynamicWrapperCS.dll /keyfile:key.snk DynamicWrapperCS.cs
C:\Windows\Microsoft.NET\Framework\v2.0.50727\regsvcs.exe DynamicWrapperCS.dll


// Requires Admin Rights to Register 
// C:\Windows\Microsoft.NET\Framework\v2.0.50727\regsvcs.exe DynamicWrapperCS.dll
*/
//https://www.add-in-express.com/creating-addins-blog/2011/12/20/type-name-system-comobject/ 

[ComVisible(true)]
[Guid("00000000-ACDC-FACE-9D8E-C0FFEEA5ACDC")]
[ClassInterface(ClassInterfaceType.AutoDispatch)]	
[ProgId("DynamicWrapperCS")]    
public class DynamicWrapperCS : ServicedComponent
{
	
	public DynamicWrapperCS() {} //Basic Constructor
	
	[ComVisible(true)]
	public Object Register(string dllName, string strReturnType,
	string methodName, string strInputParameterTypes,ref object objParameters)
	{
		
		//COM has no Type class, so do the necessary conversions
		Type returnType = Type.GetType(strReturnType);
		//Input Parameter Types
		int countOfInputParameters = (strInputParameterTypes.Length - 2);
		Type[] parameterTypes = new Type[countOfInputParameters];
		for(int i = 2, j = 0; i < strInputParameterTypes.Length; i++, j++)
		{
			parameterTypes[j] = ConvertStringNameToType(strInputParameterTypes[i]);
		}
		
		
		
		// Begin to build the dynamic assembly
		AppDomain domain = AppDomain.CurrentDomain;
		AssemblyName name = new System.Reflection.AssemblyName("PInvokeAssembly");
		AssemblyBuilder assembly = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
		ModuleBuilder module = assembly.DefineDynamicModule("PInvokeModule");
		TypeBuilder type = module.DefineType("PInvokeType", TypeAttributes.Public | TypeAttributes.BeforeFieldInit);
		 
		// Define the actual P/Invoke method
		MethodBuilder method = type.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.PinvokeImpl, returnType, parameterTypes);
		 
		// Apply the P/Invoke constructor
		ConstructorInfo ctor = typeof (DllImportAttribute).GetConstructor (new Type [] { typeof (string) });
		CustomAttributeBuilder attr = new System.Reflection.Emit.CustomAttributeBuilder(ctor, new Object[] { dllName });
		method.SetCustomAttribute(attr);
		
		
		Object[] parameters = ConvertJsArray(objParameters);
		// Create the temporary type, and invoke the method.
		Type realType = type.CreateType();
		return realType.InvokeMember(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, parameters);
		
	}
	
	private Type ConvertStringNameToType(char letter)
	{
		
		switch(letter)
		{
			case 'l':
                    return Type.GetType("System.Int32");
			case 's': 
					return Type.GetType("System.String");
			default:
					return Type.GetType("System.Object");
				
		}
	}
	
	private object[] ConvertJsArray(object jsArray)
      {
         int arrayLength =  (int) jsArray.GetType().InvokeMember("length", BindingFlags.GetProperty, null, jsArray , new object[] { });
         object[] array = new object[arrayLength];

         for (int index = 0; index < arrayLength; index++)
         {
            array[index] = jsArray.GetType().InvokeMember(index.ToString(), BindingFlags.GetProperty, null, jsArray, new object[] { });
         }

         return array;
      }
	
	
	[ComVisible(true)]
	public Object InvokeWin32(string dllName, Type returnType,
	string methodName, Type[] parameterTypes, Object[] parameters)
	{
		  
		  
		// Begin to build the dynamic assembly
		AppDomain domain = AppDomain.CurrentDomain;
		AssemblyName name = new System.Reflection.AssemblyName("PInvokeAssembly");
		AssemblyBuilder assembly = domain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
		ModuleBuilder module = assembly.DefineDynamicModule("PInvokeModule");
		TypeBuilder type = module.DefineType("PInvokeType", TypeAttributes.Public | TypeAttributes.BeforeFieldInit);
		 
		// Define the actual P/Invoke method
		MethodBuilder method = type.DefineMethod(methodName, MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static | MethodAttributes.PinvokeImpl, returnType, parameterTypes);
		 
		// Apply the P/Invoke constructor
		ConstructorInfo ctor = typeof (DllImportAttribute).GetConstructor (new Type [] { typeof (string) });
		CustomAttributeBuilder attr = new System.Reflection.Emit.CustomAttributeBuilder(ctor, new Object[] { dllName });
		method.SetCustomAttribute(attr);
		 
		// Create the temporary type, and invoke the method.
		Type realType = type.CreateType();
		return realType.InvokeMember(methodName, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, null, null, parameters);
	}
	
	[ComVisible(true)]
	public Object MessageBox(Int32 hWnd, string lpText, string lpCaption, Int32 uType) 
	{ 
	   Type[]  parameterTypes = { Type.GetType("System.Int32"), Type.GetType("System.String"),Type.GetType("System.String"),Type.GetType("System.Int32")};
	   Object[] parameters = {hWnd, lpText, lpCaption, uType};
	 
	   return InvokeWin32("user32.dll", Type.GetType("System.Int32"), "MessageBoxA", parameterTypes,  parameters );
	}
	
	
	
	
}