[mono-cecil] How to import Type: Member 'x' is declared in another module and needs to be imported.

2,303 views
Skip to first unread message

Remy Blok

unread,
Feb 27, 2012, 10:21:34 AM2/27/12
to mono-cecil
I have a class with a default constructor. Based on an assembly
attribute I'm trying to change the default constructor with additional
arguments. The type of the arguments I'm adding is located in a
different assembly.
When I've change the assembly and try to save it I get an exception,
which I cannot seem to fix: Member 'OtherAssembly.IViewModel' is
declared in another module and needs to be imported.

The assembly that contains the Type is referenced by the assembly I'm
changing. I also see this in the AssemblyReferences in the current
module. I'm importing the Type into the current module when I get the
TypeReference out of the Attribute.

It is probably someting simple but I can't seem to find it. What Am I
doing wrong?
Thanks for your help!

Below the code I'm using to test the issue.
Assembly "Views.dll" I'm changing contains:

public class Class1
{
public Class1() { }
}

[assembly: OtherAssembly.ViewModel(typeof(Class1),
typeof(OtherAssembly.IViewModel))]


The "OtherAssembly" I'm referencing contains:

namespace OtherAssembly
{
public interface IViewModel { }

[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ViewModelAttribute : Attribute
{
public ViewModelAttribute(Type viewType, Type viewModelType) { }
}
}

The Code I'm using in a separate tool to change the assembly:

var module = ModuleDefinition.ReadModule("Views.dll");

//load Assembly attribute
var attribute = module.Assembly.CustomAttributes.First(a =>
a.Constructor.DeclaringType.FullName ==
"OtherAssembly.ViewModelAttribute");
var customAttributeArguments = attribute.ConstructorArguments;

//get Arguments from the attribute
var viewType =
module.Import((TypeReference)customAttributeArguments[0].Value).Resolve(); //
Resolves Class1
var viewModelType =
module.Import((TypeReference)customAttributeArguments[1].Value).Resolve(); //
Resolves OtherAssembly.IViewModel

//find the constructor of "Class1"
var ctor = viewType.Methods.First(m => m.IsConstructor && !m.IsStatic
&& m.Parameters.Count == 0);

//add "OtherAssembly.IViewModel" parameter to constructor
var viewModelTypeParameter = new ParameterDefinition(viewModelType);
ctor.Parameters.Add(viewModelTypeParameter);

//write causes exception: Member 'OtherAssembly.IViewModel' is
declared in another module and needs to be imported
module.Write("Views2.dll");

Jb Evain

unread,
Feb 27, 2012, 10:55:29 AM2/27/12
to mono-...@googlegroups.com
Hi,

On Feb 27, 2012, at 4:21 PM, Remy Blok wrote:
> It is probably someting simple but I can't seem to find it.

Indeed it is ;)


>
> //get Arguments from the attribute
> var viewType =

> module.Import().Resolve(); //


> Resolves Class1
> var viewModelType =
> module.Import((TypeReference)customAttributeArguments[1].Value).Resolve(); //
> Resolves OtherAssembly.IViewModel

Your issue is just here: a bit of cargo cult programming.

Let's decompose:

> (TypeReference)customAttributeArguments[1].Value

You have a perfectly valid TypeReference about “OtherAssembly.IViewModel” (which is defined in OtherAssembly.dll) scoped for Views.dll

> module.Import((TypeReference)customAttributeArguments[1].Value)

This does exactly nothing. You already have a TypeReference scoped for module.

> module.Import((TypeReference)customAttributeArguments[1].Value).Resolve()

This returns the TypeDefinition of IViewModel from OtherAssembly.dll, obviously scoped for OtherAssembly.dll

As you try to use it as is, obviously Cecil complains. You have to create a reference for it that makes sense.

Let's recompose using steps that make sense.

Let start with

> var viewType = module.Import((TypeReference)customAttributeArguments[0].Value).Resolve(); // Resolves Class1

And turn it into:

> var viewType = (TypeDefinition) customAttributes.Arguments[0].Value;

Because Class1 is defined in the same module, you can use it as a TypeDefinition directly. You could also write:

> var viewType = ((TypeReference) customAttributes.Arguments[0].Value).Resolve();

But it's less explicit. Now for the second argument which is defined in another assembly:

Let's turn

> var viewModelType = module.Import((TypeReference)customAttributeArguments[1].Value).Resolve(); // Resolves OtherAssembly.IViewModel

Into:

> var viewModelType = (TypeReference) customAttributes.Arguments[1].Value;

You already have a perfectly valid TypeReference, you don't need to Import or Resolve it.

var ctor = …;
var ctor.Parameters.Add (new ParameterDefinition (viewModelType));

Bang. Fixed.

Jb

Remy Blok

unread,
Feb 27, 2012, 1:43:03 PM2/27/12
to mono-cecil
Thanks for your help! Works like a charm.

I was too focused on importing to see that it actually importing makes
it worse.
Also because TypeDefinition inherits from TypeReference, so there are
no compiler errors when using the resolved Definition.

The exception message is also not much of an help, since it points you
in the wrong direction. I'm wondering if the "new ParameterDefinition
(viewModelType)" should not have thrown an exception explaining you
need a TypeReference in stead of a TypeDefinition, in case of the type
living in another Assembly. Just a thought...

Regards, Remy

Jb Evain

unread,
Feb 27, 2012, 3:06:45 PM2/27/12
to mono-...@googlegroups.com
On Feb 27, 2012, at 7:43 PM, Remy Blok wrote:
> The exception message is also not much of an help, since it points you
> in the wrong direction. I'm wondering if the "new ParameterDefinition
> (viewModelType)" should not have thrown an exception explaining you
> need a TypeReference in stead of a TypeDefinition, in case of the type
> living in another Assembly. Just a thought...

Yeah.

That's the unfortunate consequence of a old design decision: metadata is allowed to not be connected to a module until they are attached to one. As such the API makes no immediate check.

It would be cool to solve eventually by providing factory methods to make sure all pieces of metadata are always connected to a module.

Jb

venu gopal Yela

unread,
Sep 11, 2017, 2:53:15 AM9/11/17
to mono-cecil
Hi JB,

I have a class Logger with method LogText. This is to log text. I have another class Math and has method add.

I am trying to inject Logger.LogText method call in the add method. Here is the code.

if (m.Name.Equals("add", StringComparison.CurrentCultureIgnoreCase))

{

       methodILProcessor = m.Body.GetILProcessor();

       Instruction instrMsg = methodILProcessor.Create(OpCodes.Ldstr, "method - add()");

       m.Body.Instructions.Insert(0, instrMsg);

       Type classObj = getType("Logger");//get type using reflection

       TypeReference typeRef = type.Module.Import(classObj);

       MethodReference logMethodRef = assembly.MainModule.Import(classObj.GetMethod("LogText", new Type[] { typeof(string) }));

       m.Body.Instructions.Insert(1, Instruction.Create(OpCodes.Call, logMethodRef));

}


While saving assembly, getting error as below. Can you please help and suggest where I am doing wrong?

System.Void MyApp.Logger::LogText(System.String)' is declared in another module and needs to be imported

Thanks
Venu

Jb Evain

unread,
Sep 11, 2017, 12:28:00 PM9/11/17
to mono-...@googlegroups.com
Hi,

Unfortunately it's hard to say just from this code. The logical explanation would be that the module you're using to Import a reference is not the one you're using the reference into.

Jb

--
--
--
mono-cecil
---
You received this message because you are subscribed to the Google Groups "mono-cecil" group.
To unsubscribe from this group and stop receiving emails from it, send an email to mono-cecil+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

venu gopal Yela

unread,
Sep 12, 2017, 2:14:51 AM9/12/17
to mono-...@googlegroups.com
Thank you JB! 

Got it. Method should be imported from Class.

You received this message because you are subscribed to a topic in the Google Groups "mono-cecil" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/mono-cecil/E_dlFgLDPjc/unsubscribe.
To unsubscribe from this group and all its topics, send an email to mono-cecil+unsubscribe@googlegroups.com.

venu gopal Yela

unread,
Sep 16, 2017, 5:33:27 AM9/16/17
to mono-cecil
Hi JB,

I have logging code in MethodWeaver.dll. This dll has class Lgoger and method Logtext.

Using Mono Cecil I have injected code Logger.LogText() into another exe as external CustomerApp.exe. When running code getting error like "access to method Logger.LogText() failed."

It seems MethodWeaver.dll should be added as external assembly reference into CustomerApp.exe. Can you please help?

Thank you!

Regards,
Venu
Reply all
Reply to author
Forward
0 new messages