I have MyServiceLibrary.dll which represents few classes such as UserStorageService, Storage, User. The service contains a storage and saves users into storage.
I created a new console application with new application domain inside it.
AppDomain masterDomain = AppDomain.CreateDomain("servicedomain");
string serviceLibraryPath = #"G:\Git\Service";
Assembly serviceAssembly = Assembly.LoadFrom(serviceLibraryPath);
Here I get all types which I use.
Type userType = serviceAssembly.GetType("MyServiceLibrary.User");
Type storageType = serviceAssembly.GetType("MyServiceLibrary.UserStorage");
Type userStorageServiceType = serviceAssembly.GetType("MyServiceLibrary.UserStorageService");
New instances of these types were creted into masterDomain.
var storage = masterDomain.CreateInstanceFromAndUnwrap(serviceLibraryPath, storageType.FullName);
var user = masterDomain.CreateInstanceFromAndUnwrap(serviceLibraryPath, userType.FullName);
var userStorageService = masterDomain.CreateInstanceFromAndUnwrap(
serviceLibraryPath, // assemblyFile
userStorageServiceType.FullName, // typeName
false, // ignoreCase
BindingFlags.CreateInstance, // bindingAttr
default(Binder), // binder
new object[] {storage}, // args
CultureInfo.CurrentCulture, // culture
new object[] {} // activationAttributes
);
All of my types which I used were inherited from MarshalByRefObject class.
Now I want to add my new user.
MethodInfo addMethod = userStorageServiceType.GetMethod("Add");
addMethod.Invoke(userStorageService, new object[] { user });
I got an exception:
TargetException: The object does not match the target type.
In logfile I saw that the instance of UserStorageService was created. I can call a static method of this class, but instance methos don't work.
did you try using GetType to be sure you reflected the exact type?
userStorageService.GetType().GetMethod("Add")
Related
I have a piece of code that works well for me using an obsolete function. I'll strip down to minimal parts:
private dynamic pSentFolderItems; //will reference the Items of an Outlook Folder
public void WatchEmail2(object app, string emailID)
{
pApp = app; //this is actually an Outlook Application instance
//below reference to the Items object comes back as ComObject
pSentFolderItems = pApp.Session.GetDefaultFolder(5).Items;
//get the type through Reflection on the instance
Type olItemsType = pApp.GetType().Assembly.GetType("Microsoft.Office.Interop.Outlook.ItemsClass", false, true);
//pSentFolderItems = Marshal.CreateWrapperOfType(pSentFolderItems, olItemsType);
//above works every time but is marked Obsolete!!
//http://go.microsoft.com/fwlink/?LinkID=296519
//I'm struggling to implement the generic version of the CreateWrapperOfType method, as errors occur
//and I cannot input the type parameters without errors
pSentFolderItems = Marshal.CreateWrapperOfType<ShouldBeComObjectType, ShouldBeOlItemsType>(pSentFolderItems);
//.....rest of code........
}
Here is VB Code:
Public Class OVBO
Private pApp As Object
Public pMailId As String = ""
Public itemSent As Boolean = False 'change to a map?
Public itemWatching As Boolean = False
Public pSentFolderItems As Object
Public Sub WatchEmail2(app As Object, emailID As String)
pMailId = emailID
pApp = app
If Not itemWatching Then
pSentFolderItems = pApp.Session.GetDefaultFolder(5).Items
Dim olType As Type = pApp.GetType().Assembly.GetType("Microsoft.Office.Interop.Outlook.ItemsClass", False, True)
Dim myFlags As BindingFlags = BindingFlags.Instance Or BindingFlags.Public Or BindingFlags.NonPublic
Dim evt As EventInfo = olType.GetEvent("ItemAdd", myFlags)
Dim evtHandlerType As Type = evt.EventHandlerType
Dim finalD As [Delegate] = [Delegate].CreateDelegate(evtHandlerType, Me, "MailSent2")
Dim addHandlerArgs() As Object = {finalD}
'The below works to return an Items Object(from a ComObject) that can be passed to the method handler invocation
pSentFolderItems = Marshal.CreateWrapperOfType(pSentFolderItems, olType)
'The above is obsolete, and am struggling to work with the non-obsolete method at [MSDN][1]
Dim miAddHandler As MethodInfo = evt.GetAddMethod()
miAddHandler.Invoke(pSentFolderItems, addHandlerArgs)
itemWatching = True
End If
Console.WriteLine("Watching {0}", emailID)
End Sub
'rest of class
End Class
Please note there are no references to the Outlook dll, and no Interop references.
So I am wondering if anyone can help translate the obsolete method call into the new way.
I've been trying to call a method with a string, but one side-effect is that a new object is created everytime I press the button. How can I get rid of this? I've tried using null, but no luck.
First attempt:
string methodName = cboOriginal.Text + "To" + cboConverted.Text;
Type numeralType = typeof(NumeralSystemConversion);
ConstructorInfo numeralConstructor = numeralType.GetConstructor(Type.EmptyTypes);
object numeralObject = numeralConstructor.Invoke(new object[] { });
MethodInfo numeralMethod = numeralType.GetMethod(methodName);
object numeralValue = numeralMethod.Invoke(numeralObject, new object[] { txtOriginal.Text });
txtConverted.Text = numeralValue.ToString();
numeralType = null; numeralConstructor = null; numeralObject = null;
numeralMethod = null; numeralValue = null;
Second attempt:
string methodName = cboOriginal.Text + "To" + cboConverted.Text;
convert = typeof(NumeralSystemConversion).GetMethod(methodName).Invoke(typeof(NumeralSystemConversion).GetConstructor(Type.EmptyTypes).Invoke(new object[] { }), new object[] { txtOriginal.Text });
txtConverted.Text = convert.ToString();
convert = null;
The 'convert' object is created when the app starts. And NumeralSystemConversion is a class I have created, where the methods are located.
What I am seeing is that the memory usage in the Diagnostic Tools (Visual Studio 2015 Community) increase each time I press the button.
The biggest "clean-up" I see is to create the two object arrays once (at startup) and store them in member variables:
object[] param1 = new object[] { };
object[] param2 = new object[] { null };
Then in your method:
param2[0] = txtOriginal.Text;
convert = typeof(NumeralSystemConversion).GetMethod(methodName)
.Invoke(typeof(NumeralSystemConversion).GetConstructor(Type.EmptyTypes)
.Invoke(param1), param2);
param2[0] = null;
I don't think you can prevent it from creating strings. That's the cost of doing business, I'm afraid. What might help is to use an explicit StringBuilder as another class member:
StringBuilder methodNameBuilder = new StringBuilder();
And in your function:
methodNameBuilder.Clear();
methodNameBuilder.Append(cboOriginal.Text);
methodNameBuilder.Append("To");
methodNameBuilder.Append(cboConverted.Text);
string methodName = methodNameBuilder.ToString();
Finally, the memory increases you're seeing could just be garbage, in which case GC.Collect(); should clean them up. But it's probably better just to leave it alone, in that case.
I have program with all DLLs hidden inside of one executable file. I am developing plugin support for this program.
All DLL in exe are stored in as Embedded Resources. Following code is used to access these embeded resources:
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resourceName = Assembly.GetExecutingAssembly()
.GetManifestResourceNames()
.FirstOrDefault(x => x.EndsWith(new AssemblyName(args.Name).Name + ".dll"));
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream == null)
return null;
var assemblyData = new byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
};
Plugins are loaded in separate domain for possibility to unload them:
public static T CreateInstanceInNewAppDomain<T>(bool shadowCopy = true)
{
var cdSetupInf = AppDomain.CurrentDomain.SetupInformation;
var separateDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null,
new AppDomainSetup()
{
LoaderOptimization = LoaderOptimization.MultiDomain,
ApplicationBase = cdSetupInf.ApplicationBase,
PrivateBinPath = cdSetupInf.PrivateBinPath,
ShadowCopyFiles = shadowCopy ? "true" : "false"
});
var type = typeof(T);
return (T)separateDomain.CreateInstanceFromAndUnwrap(type.Assembly.Location, type.FullName);
}
The most important part of previous code is call „CreateInstanceFromAndUnwrap“ for creation object on separate domain. This part of code would work well if DLL containing type would be on HDD. Since it is loaded from Embedded Resources the assemblyFile parameter (represented as type.Assembly.Location) is empty. Therefore I am not able to create instance of my IPluginAccessor which I am using for accessing functionality in plugins. Is there any way how can create instance of type in separate domain without need to specify assemblyName? It would be great if there would be something like AppDomain.CreateInstanceFromAndUnwrap(Type type). If I looked well, there is only Activator.CreateInstance(Type type) but this works for current domain and not one I specify.
Thanks a lot
I think you can use Callback which will be executed in the new AppDomain.
Define an helper class:
class AnotherAppDomainPluginBuilder<T> : MarshalByRefObject
where T : new()
{
T Result { get; set; }
public void Create()
{
LoadRelevantDlls();
this.Result = new T();
}
void LoadRelevantDlls()
{
// load whatever DLLs in whatever way you would like to
}
}
Then instead of:
return (T)separateDomain.CreateInstanceFromAndUnwrap(type.Assembly.Location, type.FullName);
Write:
AnotherAppDomainPluginBuilder<T> builder =
(T)separateDomain.CreateInstanceFromAndUnwrap(
Assembly.CurrentAssembly,
typeof(AnotherAppDomainPluginBuilder<T> ));
separateDomain.DoCallback(builder.Create);
return builder.Result;
I'm using RhinoMocks for testing. It's not good at redirecting statics; I've considered using another library like the successor to Moles (edit: I guess the Fakes tool is only available in VS2012? that stinks) or TypeMock, but would prefer not to.
I have a 3rd party library that takes in an HttpRequest object. My first stab at it was to use:
public void GetSamlResponseFromHttpPost(out XmlElement samlResponse,
out string relayState, HttpContextBase httpContext = null)
{
var wrapper = httpContext ?? new HttpContextWrapper(HttpContext.Current);
// signature of the next line cannot be changed
ServiceProvider.ReceiveSAMLResponseByHTTPPost(
wrapper.ApplicationInstance.Context.Request, out samlResponse, out relayState);
All looked fine, until I went to test it. The real issue here is that I need to stub out wrapper.ApplicationInstance.Context.Request. Which leads into a whole host of old school 'ASP.NET don't like testing' pain.
I have heard that you can use dynamic magic in C# to redirect static methods. Can't find any examples of doing this with something like HttpContext though. Is this possible?
Not an ideal solution, but my solution to test this was to use reflection and modify the object under the hood:
httpRequest = new HttpRequest("default.aspx", "http://test.com", null);
var collection = httpRequest.Form;
// inject a value into the Form directly
var propInfo = collection.GetType().GetProperty("IsReadOnly", BindingFlags.Instance | BindingFlags.NonPublic);
propInfo.SetValue(collection, false, new object[] { });
collection["theFormField"] = val;
propInfo.SetValue(collection, true, new object[] { });
var appInstance = new HttpApplication();
var w = new StringWriter();
httpResponse = new HttpResponse(w);
httpContext = new HttpContext(httpRequest, httpResponse);
// set the http context on the app instance to a new value
var contextField = appInstance.GetType().GetField("_context", BindingFlags.Instance | BindingFlags.NonPublic);
contextField.SetValue(appInstance, httpContext);
Context.Stub(ctx => ctx.ApplicationInstance).Return(appInstance);
My goal here was to have wrapper.ApplicationInstance.Context.Request return a form field value when asked. It may have been roundabout, but it works. This code only exists in test code so I'm happy with it.
given a url that references an asmx how would i go about displaying all of their method names?
if assembly="http://.../something/something.asmx" and i was trying to display the method names of that service what should i do now that i have gotten myself this far? i cant seem to find a solution among the hundreds of examples ive looked at
public TestReflection(string assembly)
{
Assembly testAssembly = Assembly.LoadFrom(assembly);
Type sType = testAssembly.GetType();
MethodInfo[] methodInfos = typeof(Object).GetMethods();
foreach (MethodInfo methodInfo in methodInfos)
{
Console.WriteLine(methodInfo.Name);
}
}
typeof(Object).GetMethods()
youre asking for all the methods of type object
you need to call GetMethods() on the type you want to get the methods of.
Try this:
public TestReflection(string assembly)
{
Assembly testAssembly = Assembly.LoadFrom(assembly);
Type sType = testAssembly.GetType("NamespaceOfYourClass.NameOfYourClassHere", true, true);
MethodInfo[] methodInfos = sType.GetMethods();
foreach (MethodInfo methodInfo in methodInfos)
{
Console.WriteLine(methodInfo.Name);
}
}
The idea is that in your original code, you're trying to get the methods by using typeof(Object), which will retrieve the methods of the Object type, which is not what you want.
You need to know what class the methods you're trying to grab are in. If you don't know that, replace testAssembly.GetType(... with testAssembly.GetTypes() and iterate through all the types, and getting the methods for each one.
You know, reflection aside, you can actually query the webservice's WSDL to get a list of methods. It may simplify your problem. If you're set on using reflection you'll have to find the type in the assembly and grab the methods using the other methods described in the other answers here.
You would need to look for method decorated with the [WebMethod] attribute on classes that inherit from System.Web.Services.WebService.
The code would look something like this (not tested):
public TestReflection(string assembly)
{
Assembly testAssembly = Assembly.LoadFrom(assembly); // or .LoadFile() here
foreach (Type type in testAssembly.GetTypes())
{
if (type.IsSubclassOf(typeof(System.Web.Services.WebService)))
{
foreach (MethodInfo methodInfo in type.GetMethods())
{
if (Attribute.GetCustomAttribute(methodInfo, typeof(System.Web.Services.WebMethodAttribute)) != null)
{
Console.WriteLine(methodInfo.Name);
}
}
}
}
}
so i figured out how to get what i wanted it goes something like this
[SecurityPermissionAttribute(SecurityAction.Demand, Unrestricted = true)]
internal static void LoadWebService(string webServiceAsmxUrl)
{
ParseUrl(webServiceAsmxUrl);
System.Net.WebClient client = new System.Net.WebClient();
// Connect To the web service
System.IO.Stream stream = client.OpenRead(webServiceAsmxUrl + "?wsdl");
// Now read the WSDL file describing a service.
ServiceDescription description = ServiceDescription.Read(stream);
///// LOAD THE DOM /////////
// Initialize a service description importer.
ServiceDescriptionImporter importer = new ServiceDescriptionImporter();
importer.ProtocolName = "Soap12"; // Use SOAP 1.2.
importer.AddServiceDescription(description, null, null);
// Generate a proxy client.
importer.Style = ServiceDescriptionImportStyle.Client;
// Generate properties to represent primitive values.
importer.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateProperties;
// Initialize a Code-DOM tree into which we will import the service.
CodeNamespace nmspace = new CodeNamespace();
CodeCompileUnit unit1 = new CodeCompileUnit();
unit1.Namespaces.Add(nmspace);
// Import the service into the Code-DOM tree. This creates proxy code that uses the service.
ServiceDescriptionImportWarnings warning = importer.Import(nmspace, unit1);
if (warning == 0) // If zero then we are good to go
{
// Generate the proxy code
CodeDomProvider provider1 = CodeDomProvider.CreateProvider("CSharp");
// Compile the assembly proxy with the appropriate references
string[] assemblyReferences = new string[5] { "System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll" };
CompilerParameters parms = new CompilerParameters(assemblyReferences);
CompilerResults results = provider1.CompileAssemblyFromDom(parms, unit1);
// Check For Errors
if (results.Errors.Count > 0)
{
foreach (CompilerError oops in results.Errors)
{
System.Diagnostics.Debug.WriteLine("========Compiler error============");
System.Diagnostics.Debug.WriteLine(oops.ErrorText);
}
Console.WriteLine("Compile Error Occured calling webservice. Check Debug ouput window.");
}
// Finally, add the web service method to our list of methods to test
//--------------------------------------------------------------------------------------------
object service = results.CompiledAssembly.CreateInstance(serviceName);
Type types = service.GetType();
List<MethodInfo> listMethods = types.GetMethods().ToList();
}
}
Paste http://.../something/something.asmx in your browser and it will give you a list of all the methods and its parameters?