I'm creating an add-on system for a shell I'm developing using C#. I've followed this and this. Here is my function to load an add-on:
public void loadAppFromDLL(string assemblyFile)
{
Assembly a = Assembly.Load(assemblyFile);
Type app = a.GetType("App");
MethodInfo loadMethod = app.GetMethod("load");
object appInstance = Activator.CreateInstance(app);
loadMethod.Invoke(appInstance, null);
}
Here is the add-on:
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace App
{
public class App
{
public void load()
{
MessageBox.Show("Application loaded successfully!");
}
}
}
When I build the add-on, I place it in the same directory as the shell executable and call:
LoadExternalApp lea = new LoadExternalApp();
lea.loadAppFromDLL("SampleApp");
(LoadExternalApp contains the DLL loading function)
When I was debugging my shell, I noticed that:
The app didn't start
There was a System.NullReferenceException
What am I not doing right?
This:
Type app = a.GetType("App");
is looking for a type with a namespace-qualified name of App.
Your type is called App in a namespace of App, so Assembly.GetType is returning null, and then you're dereferencing it. Instead, you should use:
Type app = a.GetType("App.App");
However, you shouldn't give a class the same name as its namespace in the first place. Fix that, so that you end up with something more like:
Type app = a.GetType("App.PlugIn");
You should still check whether GetType (or GetMethod) returns null, in order to fail rather more gracefully and with more information.
Additionally, you should start following .NET naming conventions - give methods names in PascalCase. Oh, and you might want to consider a common interface for your add-ins rather than relying on reflection to call methods.
Related
I'm working on a cross platform app using the GTK toolkit and when I'm trying to access the System.IO namespace to extract Path fields, I'm getting the following error:
Error CS0119: 'Widget.Path(out uint, out string, out string)' is a method, which is not valid in the given context (CS0119) (netmonmd)
I have using System.IO; in the source file and I'm using other namespaces without any issue (eg System.Console and System.Refelection without any issues further down in the code).
I have almost identical code in a console application working without an issue.
Here is the code, if I fully provide the Path it works, I just can't use it within the namespace (but I can use other things that are in the same namespace)
using Gtk;
using System;
using System.IO;
using static System.Console;
public partial class MainWindow : Gtk.Window
{
private netmonmd.iplabel[] ids;
private int fSize = 12;
public MainWindow() : base(Gtk.WindowType.Toplevel)
{
Build();
//char filesep = System.IO.Path.DirectorySeparatorChar; // works
char filesep = Path.DirectorySeparatorChar; // error
I'm trying to understand the error (I am new to C#) and think it has to do with calling it within the MainWindow function since it seems to be referring to Widget.Path and not System.Path
Is this a case of just having to fully qualify it each time?
I could move all the code out of the main window and process it elsewhere in the app.cs file, however it is a single window app that I was developing in the one space. If it is better practise to separate it out I will do that.
(as is usually the case in asking on stack, while writing this question out I'm becoming more aware of what's actually happening here, I just don't know how to work around it)
I'm creating a program that uses the CodeProject CoreAudioApi (pretty popular framework for manipulating audio), but the problem is the CoreAudioApi uses system calls that aren't available in any versions of Windows earlier than Vista. If I run a program with CoreAudioApi compiled with it (using a using statement as normal), the program will crash on anything earlier than Vista.
I've created this function to get the version number of the current environment:
win_version = Environment.OSVersion.Version.Major;
That returns the major version number I need. '6' is Vista/7, anything else is not, which is all I need to determine. Utilizing this, I need to determine whether or not to include the CoreAudioApi namespace if the OS is over or equal to '6'. From research, usings need to be compiled with the program, but I've also read about something called Reflection - which might be what I need.
Once I get the CoreAudioApi namespace using'd (sorry for the lack of terminology), the rest is easy. How can I do this?
TL;DR
I need some form of code that would effectively do this:
using System;
using System.Text;
//etc
if(currentWindowsVersion>=6) using CoreAudioApi;
Except control structures won't work outside of a class, and all namespaces are compiled with the program, not controlled individually.
Thanks!
EDIT: So far, I'm using this to load the CoreAudioApi namespace as a compiled assembly:
if(win_version>=6){
CoreAudioApi = Assembly.LoadFrom("CoreAudio.dll");
CoreAudioApi.GetLoadedModules();
CoreAudioApi.GetTypes();
MessageBox.Show("Loaded CoreAudioApi");
}
From here, what I need to do is actually use the types, and methods from the API. My code that works on Windows Vista/7 is this:
public static MMDeviceEnumerator devEnum;
public static MMDevice defaultDevice;
//later in a mute method:
defaultDevice.AudioEndpointVolume.Mute = true/false;
I don't even really need devEnum AFAIK, so really the only important lines are the last two (besides the comment).
I've just tried the following:
Create a new console application project
Add the CoreAudioApi project from CodeProject to the solution
Add a project reference to CoreAudioApi in my console app
Create the following classes:
interface IAudio { void SetVolume(float level); }
class XpAudio : IAudio {
public void SetVolume(float level) {
// I do nothing, but this is where your old-style code would go
}
}
class VistaAudio : IAudio {
public void SetVolume(float level) {
MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
MMDevice defaultDevice = devEnum
.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
defaultDevice.AudioEndpointVolume.MasterVolumeLevel = level;
}
}
class Program {
static void Main(string[] args) {
IAudio setter = Environment.OSVersion.Version.Major >= 6
? (IAudio)new VistaAudio()
: (IAudio)new XpAudio();
float val = float.Parse(Console.ReadLine());
setter.SetVolume(val);
Console.ReadLine();
}
}
This runs on both my server (~ Windows 7) and local (Windows XP) machines. On my XP machine it'll happily take in a value and ignore it; on my server, it throws an exception, (presumably because I don't have a sound output). If I make my XP machine run the CoreAudioApi, I get an exception when I input a value, not before.
The question is, what are you doing differently to make your application break? Are you using CoreAudioApi code at startup?
EDIT: After seeing your edit, if you do this, you shouldn't need to mess about with Assembly.LoadFrom at all. The framework should dynamically load that assembly if (and only if) and when it needs to.
COREAUDIOAPI.dll does not work on XP or earlier, because they cant handle MMDEVICE API (Device Enumeration). I dont know about Vista.
I have a Mono for Android project that compiles and runs successfully with the following code. However a copy/paste into a newer project I'm working on results in a compiler error indicating: [appnamespace].Android.Resource.Layout does not contain a definition for 'SimpleListItem2'.
I'd expect that error if I was trying to access an xml resource layout I defined, but I'm trying to access the default ones provided by Google, specifically 'SimpleListItem2'. Is there something I need to do get the compiler to recognize the default layouts? Thanks!
public override View GetView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null) {
LayoutInflater li = (LayoutInflater)this.Context.GetSystemService(Context.LayoutInflaterService);
v = li.Inflate(Android.Resource.Layout.SimpleListItem2, null);
}
TextView tt = (TextView)v.FindViewById(Android.Resource.Id.Text1);
if (tt != null) { tt.Text = string.Format("{0}, {1}", this.LastName, this.FirstName); }
return v;
}
Basically, I'm looking for access to these layouts in the Mono.Android assembly, under the Android.Resource.Layout namespace:
ActivityListItem
BrowserLInkContextHeader
ExpandableListContent
PreferenceCategory
SelectDialogItem
SelectDialogMultiChoice
SelectDialogSingleChoice
SimpleDropDownItem1Line
SimpleExpandableListItem1
SimpleExpandableListItem2
SimpleGalleryItem
SimpleListItem1
SimpleListItem2
SimpleListItemChecked
SimpleListItemMultipleChoice
SimpleListItemSingleChoice
SimpleListItemDropDownItem
SimpleSpinnerItem
TestListItem
TwoLineListItem
This is a C# language feature, and is behaving as per the C# language specification.
Consider this example:
using System;
namespace Example {
namespace System {
}
class Bad : System.Object {
}
}
The above fails to compile:
ns.cs(8,24): error CS0234: The type or namespace name `Object' does not exist in
the namespace `Example.System'. Are you missing an assembly reference?
This is true for both .NET CSC and Mono's mcs compilers.
Why? See ยง10.8 Namespace and type names of the C# Language Specification, pages 100-102.
Otherwise, the namespace-or-type-name is of the form N.I or of the form N.I<A1, ..., AK>.
N is first resolved as a namespace-or-type-name.
In this case we're processing System.Object, which is of the form N.I. So
we need to first resolve System:
Otherwise, if the namespace-or-type-name is of the form I or of the form I<A1, ..., AK>:
...
Otherwise, for each namespace N, starting with the namespace in which the
namespace-or-type-name occurs, continuing with each enclosing namespace
(if any), and ending with the global namespace, the following steps are evaluated
until an entity is located:
...
This resolves the token System to Example.System. Now that System is
resolved the compiler attempts to resolve System.Object, i.e. the fully
qualified name of Example.System.Object. This type does not exist, and we get
an error.
The fix for the above sample? Use global:::
class Bad : global::System.Object {
}
The same is true for your Android code; if you happen to be within an
Example.Android namespace and you need to use the Android.Resource.Layout
type, then use global::Android.Resource.Layout.
Or you can use a using-alias, which is resolved at the point of declaration, allowing:
using System;
using MyObject = System.Object;
namespace Example {
namespace System {
}
class Bad : MyObject {
}
}
It appears that namespace collisions causes this error. Be warned when using "Android" in your namespace.
If the app's namespace includes "Android", Xamarin's Mono for Android compiler seems to struggle resolving Google's SimpleListItem2 object.
To experience the error, the "Android" string needs to be a complete string (not substring) in the namespace. The namespace "BenHorgen.Android.MyApps.TestApp" will cause the compiler error.
More specifically, including the string "Android" as substring in the namespace will not cause the issue. For example: "BenHorgen.MyAndroidApps.TestApp" does not cause the problem for me.
Does anyone know if there's a way to hook into an "OnLoad" event to run some operations when an assembly loads?
Specifically, I am creating a plug-in for an application. The plug-in's DLL gets loaded and objects start being used, but the problem is I need to load another assembly dynamically before anything happens. This assembly can't be copied to the application's directory and must remain invisible to it.
You need to hook on to AssemblyLoad event.
Refer-
http://msdn.microsoft.com/en-us/library/system.appdomain.assemblyload.aspx
It is really sad that writing a Main() function in an Assembly DLL is never called by the .NET framework.
It seems that Microsoft forgot that.
But you can easily implement it on your own:
In the DLL assembly you add this code:
using System.Windows.Forms;
public class Program
{
public static void Main()
{
MessageBox.Show("Initializing");
}
}
Then in the Exe Assembly that loads this DLL you add this function:
using System.Reflection;
void InitializeAssembly(Assembly i_Assembly)
{
Type t_Class = i_Assembly.GetType("Program");
if (t_Class == null)
return; // class Program not implemented
MethodInfo i_Main = t_Class.GetMethod("Main");
if (i_Main == null)
return; // function Main() not implemented
try
{
i_Main.Invoke(null, null);
}
catch (Exception Ex)
{
throw new Exception("Program.Main() threw exception in\n"
+ i_Assembly.Location, Ex);
}
}
Obviously you should call this function at the very beginning before doing anything else with that Assembly.
C# does not provide a way to do that but the underlying IL code does via module initializers. You can use tools like Fody/ModuleInit to turn a specially named static C# class to run as a module initializer which will be run when your dll is loaded.
I am trying to get the executing assembly version in C# 3.0 using the following code:
var assemblyFullName = Assembly.GetExecutingAssembly().FullName;
var version = assemblyFullName .Split(',')[1].Split('=')[1];
Is there another proper way of doing so?
Two options... regardless of application type you can always invoke:
Assembly.GetExecutingAssembly().GetName().Version
If a Windows Forms application, you can always access via application if looking specifically for product version.
Application.ProductVersion
Using GetExecutingAssembly for an assembly reference is not always an option. As such, I personally find it useful to create a static helper class in projects where I may need to reference the underlying assembly or assembly version:
// A sample assembly reference class that would exist in the `Core` project.
public static class CoreAssembly
{
public static readonly Assembly Reference = typeof(CoreAssembly).Assembly;
public static readonly Version Version = Reference.GetName().Version;
}
Then I can cleanly reference CoreAssembly.Version in my code as required.
In MSDN, Assembly.GetExecutingAssembly Method, is remark about method "getexecutingassembly", that for performance reasons, you should call this method only when you do not know at design time what assembly is currently executing.
The recommended way to retrieve an Assembly object that represents the current assembly is to use the Type.Assembly property of a type found in the assembly.
The following example illustrates:
using System;
using System.Reflection;
public class Example
{
public static void Main()
{
Console.WriteLine("The version of the currently executing assembly is: {0}",
typeof(Example).Assembly.GetName().Version);
}
}
/* This example produces output similar to the following:
The version of the currently executing assembly is: 1.1.0.0
Of course this is very similar to the answer with helper class "public static class CoreAssembly", but, if you know at least one type of executing assembly, it isn't mandatory to create a helper class, and it saves your time.
using System.Reflection;
{
string version = Assembly.GetEntryAssembly().GetName().Version.ToString();
}
Remarks from MSDN http://msdn.microsoft.com/en-us/library/system.reflection.assembly.getentryassembly%28v=vs.110%29.aspx:
The GetEntryAssembly method can return null when a managed assembly has been loaded from an unmanaged application. For example, if an unmanaged application creates an instance of a COM component written in C#, a call to the GetEntryAssembly method from the C# component returns null, because the entry point for the process was unmanaged code rather than a managed assembly.
Product Version may be preferred if you're using versioning via GitVersion or other versioning software.
To get this from within your class library you can call System.Diagnostics.FileVersionInfo.ProductVersion:
using System.Diagnostics;
using System.Reflection;
//...
var assemblyLocation = Assembly.GetExecutingAssembly().Location;
var productVersion = FileVersionInfo.GetVersionInfo(assemblyLocation).ProductVersion
This should do:
Assembly assem = Assembly.GetExecutingAssembly();
AssemblyName aName = assem.GetName();
return aName.Version.ToString();
I finally settled on typeof(MyClass).GetTypeInfo().Assembly.GetName().Version for a netstandard1.6 app. All of the other proposed answers presented a partial solution. This is the only thing that got me exactly what I needed.
Sourced from a combination of places:
https://msdn.microsoft.com/en-us/library/x4cw969y(v=vs.110).aspx
https://msdn.microsoft.com/en-us/library/2exyydhb(v=vs.110).aspx