System.InvalidProgramException using OpCodes.Ldsfld w/Guid.Empty.

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

All,

I am running into an exception using IlGenerator to emit the body of a method.

The method takes a System.Guid as the only argument. I am trying to test to make sure that it isn't an empty Guid.

So, I write:


FieldInfo fi_Empty = typeof(System.Guid).GetField("Empty");
generator.Emit(OpCodes.Ldsfld, fi_Empty);

According to what I have read, this should put he value of the static field "Empty" which is of type System.Guid onto the stack.

What I get instead is an InvalidProgramException: "The Common Language Runtime has detected an Invalid Program"

Can anyone tell me what is wrong with the FieldInfo or the use of the empty Guid?

The rest of the method works fine.

My forehead and my wall both thank you in advance for any guidance.
 
Can you post the rest of the method's code? Have you run PEVerify on
the executable?



Mattias
 
Here is the rest of the code:

If I remove the first section which loads the empty GUID onto the stack...then checks it against the inbound GUID in LdArg_1 (it is an instance class) the rest of the code works fine.

/// <summary>
///
/// </summary>
/// <param name="xTypeBuilder"></param>
/// <param name="xType"></param>
private void generateFindByParentID(ref TypeBuilder xTypeBuilder, Type xType)
{

//Get the DataSource Attribute attached to the object.
Shared.Data.DataSource[] DataSourceArray = (Shared.Data.DataSource[])
xType.GetCustomAttributes(typeof(Shared.Data.DataSource),true);

string tmpTable = DataSourceArray[0].Table;

//Create a Method on the new type.
MethodBuilder FindByParentIDMethod = xTypeBuilder.DefineMethod ("FindByParentID",MethodAttributes.Public|MethodAttributes.Virtual,
typeof(System.Collections.ArrayList),new Type[]{typeof(System.Guid)});

//Get the IL Generator for the Method Builder.
ILGenerator generator = FindByParentIDMethod.GetILGenerator();

//Define the local variables that will be used to manage the stack.
LocalBuilder strSQL = generator.DeclareLocal(typeof(System.String));
LocalBuilder returnTypes = generator.DeclareLocal(typeof(System.Collections.ArrayList));
LocalBuilder ds = generator.DeclareLocal(typeof(System.Data.DataSet));
LocalBuilder dr = generator.DeclareLocal(typeof(System.Data.DataRow));
LocalBuilder returnType = generator.DeclareLocal(typeof(Shared.Objects.EBCObject));
LocalBuilder tmp_returnTypes = generator.DeclareLocal(typeof(System.Collections.ArrayList));
LocalBuilder tmp_Guid = generator.DeclareLocal(typeof(System.Guid));
LocalBuilder tmp_IEnumerator = generator.DeclareLocal(typeof(System.Collections.IEnumerator));
LocalBuilder tmp_IDisposable = generator.DeclareLocal(typeof(System.IDisposable));

//Define the Labels that are inserted into the executing stream to define the flow of
//execution.
Label lblMoveNext = generator.DefineLabel();
Label lblCurrent = generator.DefineLabel();
Label lblEndMethod = generator.DefineLabel();
Label lblBeginMethod = generator.DefineLabel();

//Get the FieldInfo object to an empty Guid.
FieldInfo fi_EmptyGuid = typeof(System.Guid).GetField("Empty",BindingFlags.Public|BindingFlags.Static);

generator.Emit(OpCodes.Ldsfld, fi_EmptyGuid);

//Store an empty guid in the tmpGuid local.
generator.Emit(OpCodes.Stloc,tmp_Guid);
generator.Emit(OpCodes.Ldloc,tmp_Guid);

//Load the Guid passed to the method onto the stack
generator.Emit(OpCodes.Ldarg_1);

//Since the Equals method compares objects not value types...box the guid.
generator.Emit(OpCodes.Box, typeof(System.Guid));

//Define the MethodInfo for the Equals operation.
MethodInfo mi_Equals = typeof(System.Guid).GetMethod("Equals", new Type[] {typeof(object)});

generator.Emit(OpCodes.Call, mi_Equals);

//If the passed-in guid is not equal to the empty guid...transfer
//control to the Begin Method Label.
generator.Emit(OpCodes.Brfalse_S, lblBeginMethod);

generator.Emit(OpCodes.Ldstr, "xParentID");
generator.Emit(OpCodes.Ldstr, "The ParentID cannot be null");

generator.Emit(OpCodes.Newobj, typeof(System.ArgumentNullException)
.GetConstructor(new Type[]{typeof(System.String), typeof(System.String)}));

generator.Emit(OpCodes.Throw);

//METHOD START
generator.MarkLabel(lblBeginMethod);

//Create the SQL Statement
generator.Emit(OpCodes.Ldstr,"SELECT * FROM " + tmpTable + " WHERE PARENTID = '");
generator.Emit(OpCodes.Ldarg_1);
generator.Emit(OpCodes.Box, typeof(System.Guid));
generator.Emit(OpCodes.Ldstr, "'");
generator.Emit(OpCodes.Call,typeof(System.String).GetMethod("Concat",new Type[3]{typeof(object), typeof(object), typeof(object)}));


generator.Emit(OpCodes.Stloc, strSQL);

//Define the ArrayList that will hold the returned objects.
generator.Emit(OpCodes.Newobj, typeof(System.Collections.ArrayList).GetConstructor(new Type[0]));

generator.Emit(OpCodes.Stloc, returnTypes);

//Fill the dataset using the SQLHelper.
generator.Emit(OpCodes.Ldloc,strSQL);
generator.Emit(OpCodes.Ldstr, tmpTable);
MethodInfo mi_Fill = typeof(Shared.Data.SQLHelper).GetMethod("FillDataSet",new Type[]{ typeof(System.String), typeof(System.String)});

generator.Emit(OpCodes.Call,mi_Fill);

//Store the dataset in the local variable.
generator.Emit(OpCodes.Stloc,ds);

//Load the DataSet back onto the stack.
generator.Emit(OpCodes.Ldloc,ds);

//Call the get method for the 'Tables' property on the DataSet.
MethodInfo mi_GetTables = typeof(System.Data.DataSet).GetProperty("Tables").GetGetMethod();

generator.Emit(OpCodes.Callvirt,mi_GetTables);

//Get the Table in the 0 index position in the Item property of the returned DataTable.

generator.Emit(OpCodes.Ldc_I4_0);
MethodInfo mi_GetItem = typeof(System.Data.DataTableCollection).GetProperty("Item",new Type[] {typeof(System.Int32)}).GetGetMethod();

generator.Emit(OpCodes.Callvirt,mi_GetItem);

//Get the collection of rows from the DataTable.
MethodInfo mi_GetRows = typeof(System.Data.DataTable).GetProperty("Rows").GetGetMethod();

generator.Emit(OpCodes.Callvirt,mi_GetRows);

//Get the enumerator for the collection of DataRows.
MethodInfo mi_GetEnumerator = typeof(System.Data.InternalDataCollectionBase).GetMethod("GetEnumerator");

generator.Emit(OpCodes.Callvirt,mi_GetEnumerator);

//Store the enumerator in the local variable.
generator.Emit(OpCodes.Stloc, tmp_IEnumerator);

//Go to the section of code marked with the label "lblMoveNext"
generator.Emit(OpCodes.Br_S, lblMoveNext);

//Mark this section of code as "lblCurrent"
//*****************************************************************
generator.MarkLabel(lblCurrent);

//Put the Enumerator on the top of the stack
generator.Emit(OpCodes.Ldloc, tmp_IEnumerator);

//Call the get method on the Current Property of the enumerator.
MethodInfo mi_GetCurrent = typeof(System.Collections.IEnumerator).GetProperty("Current").GetGetMethod();

generator.Emit(OpCodes.Callvirt,mi_GetCurrent);

//Cast the return type to a datarow and store it in the local variable.
generator.Emit(OpCodes.Castclass,typeof(System.Data.DataRow));
generator.Emit(OpCodes.Stloc, dr);
generator.Emit(OpCodes.Ldloc, dr);

//Load the string "ObjectID" onto the stack as the string that is related to the Item indexer
//of the DataRow. Then use it to get the value of that column.
generator.Emit(OpCodes.Ldstr, "ObjectID");
MethodInfo mi_GetDataRowItem = typeof(System.Data.DataRow).GetProperty("Item",new Type[]{ typeof(System.String)}).GetGetMethod();
generator.Emit(OpCodes.Callvirt, mi_GetDataRowItem);

//Items are stored as objects inside a datarow, so unbox the contained GUID.
generator.Emit(OpCodes.Unbox, typeof(System.Guid));
generator.Emit(OpCodes.Ldobj, typeof(System.Guid));

//Use the GUID to create a new instance of the target type using the constructor that takes a GUID.
ConstructorInfo xType_Ctor = xType.GetConstructor(new Type[]{typeof(System.Guid)});

generator.Emit(OpCodes.Newobj, xType_Ctor);

//Cast the type as an EBCObject.
generator.Emit(OpCodes.Castclass, typeof(Shared.Objects.EBCObject));

//Store the returned type in the local variable.
generator.Emit(OpCodes.Stloc, returnType);
generator.Emit(OpCodes.Ldloc, returnType);

//Call the "Load" method on the returned type that takes a DataRow.
generator.Emit(OpCodes.Ldloc, dr);

MethodInfo mi_Load_DR = xType.GetMethod("load", new Type[]{ typeof(System.Data.DataRow)});

generator.Emit(OpCodes.Callvirt, mi_Load_DR);

//load the Arraylist and the returned type onto the stack.
generator.Emit(OpCodes.Ldloc, returnTypes);
generator.Emit(OpCodes.Ldloc, returnType);

//Call Add on the Arraylist.
MethodInfo mi_Add = typeof(System.Collections.ArrayList).GetMethod("Add", new Type[] { typeof(System.Object)});

generator.Emit(OpCodes.Callvirt, mi_Add);

//Pop the top item off the stack.
generator.Emit(OpCodes.Pop);

//Mark this section of code as "lblMoveNext"
//*****************************************************************
generator.MarkLabel(lblMoveNext);

//Load the enumerator onto the stack.
generator.Emit(OpCodes.Ldloc, tmp_IEnumerator);

//Call movenext on the enumerator.
MethodInfo mi_MoveNext = typeof(System.Collections.IEnumerator).GetMethod("MoveNext");

generator.Emit(OpCodes.Callvirt, mi_MoveNext);

//If it returns true, then goto the section of code marked "lblCurrent"
generator.Emit(OpCodes.Brtrue, lblCurrent);

//When there are no rows left, return the filled ArrayList.
generator.Emit(OpCodes.Ldloc, returnTypes);
generator.Emit(OpCodes.Ret);

//Get the Create method from the IFactory interface.
MethodInfo FactoryFindByParentID =typeof(IFactory).GetMethod("FindByParentID");

//Override the IFactory create method with our implementation.
xTypeBuilder.DefineMethodOverride(FindByParentIDMethod, FactoryFindByParentID);
}
 
//Store an empty guid in the tmpGuid local.
generator.Emit(OpCodes.Stloc,tmp_Guid);
generator.Emit(OpCodes.Ldloc,tmp_Guid);

//Load the Guid passed to the method onto the stack
generator.Emit(OpCodes.Ldarg_1);

//Since the Equals method compares objects not value types...box the guid.
generator.Emit(OpCodes.Box, typeof(System.Guid));

//Define the MethodInfo for the Equals operation.
MethodInfo mi_Equals = typeof(System.Guid).GetMethod("Equals", new Type[] {typeof(object)});

generator.Emit(OpCodes.Call, mi_Equals);


To call Guid.Equls, you should have a Guid* to the callee on the
stack, so you must replace the ldloc instruction above with ldloca or
ldloca.s.



Mattias
 
I will try this, but I am curious...
to debug, I commented out code, then starting from the top I uncommented code line by line.

The original error is generated by the Ldsfld. Are you suggesting that I load an address instead? I haven't found any information about not loading an empty reference to a Guid using Ldsfld.

Do you know of any reason why the load of that static field would cause a problem?


Thanks for taking an interest in this problem.

Mattias Sjögren said:
//Store an empty guid in the tmpGuid local.
generator.Emit(OpCodes.Stloc,tmp_Guid);
generator.Emit(OpCodes.Ldloc,tmp_Guid);

//Load the Guid passed to the method onto the stack
generator.Emit(OpCodes.Ldarg_1);

//Since the Equals method compares objects not value types...box the guid.
generator.Emit(OpCodes.Box, typeof(System.Guid));

//Define the MethodInfo for the Equals operation.
MethodInfo mi_Equals = typeof(System.Guid).GetMethod("Equals", new Type[] {typeof(object)});

generator.Emit(OpCodes.Call, mi_Equals);


To call Guid.Equls, you should have a Guid* to the callee on the
stack, so you must replace the ldloc instruction above with ldloca or
ldloca.s.



Mattias
 
The original error is generated by the Ldsfld. Are you suggesting that I load an address instead?
Exactly


I haven't found any information about not loading an empty reference to a Guid using Ldsfld.

Guid is a value type, so there's no reference involved. The value on
the stack becomes the "this" pointer in the called method. That's why
you must load an address with ldloca for value types, but can use
ldloc for reference type variables.



Mattias
 
Thank you Mattias.

I have it running now. It was ended up being a comedy of errors. The first of which was my ignorance of the facts you presented.

I changed the code so that I was loading an address to the static field...

generator.Emit(OpCodes.Ldsflda, myFieldInfo);

Then, I stopped trying to store the pointer in the LocalBuilder of type System.Guid.

It is happy now. And I can stop hitting the wall...

Thanks again!
 
Back
Top