MSIL Property Setter - Access to the value field - cil

I have the following setter-method, but the object I put in value isn't put through to the called method:
.method public hidebysig specialname instance void set_SeatingCapacity(int32 'value') cil managed
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
.maxstack 3
L_0000: ldc.i4 0x6c
L_0005: ldarg.0
L_0006: ldfld int32 Young3.FMSearch.Core.Entities.InGame.BaseObject::MemoryAddress
L_000b: ldarg.1
L_000c: call void Young3.FMSearch.Core.Managers.PropertyInvoker::Set(int32, int32, object)
L_0011: ret
}
I want to call the function in L_000c like Set(0x6c, ldfld MemoryAddress, value). The first two fields are correctly posted to the function. Any clue? It looks quite well when doing something similar and looking at the definition in Reflector.

I had to do a box int32, or by making Set into Set<T>.

Related

Emitted IL NullReferenceException on call to method on Int32

I'm new to IL in .NET, and am messing around trying to autogenerate a method that is pure boilerplate.
My test app generates the below IL, but it is throwing a NullReferenceException at IL_002f, after retrieving the value of an int property on an instance which is known not to be NULL (both via the IL (IL_0001) and from the test case I generated). The instance is passed in the first argument (arg.0) to the emitted method.
I expect the value of BasicClass.IntProperty to be the first item on the stack before calling System.Int32.ToString(), so what is possibly going wrong here? Any help to get my head out of my ahem on this would be appreciated.
IL_0000: ldarg.0
IL_0001: brfalse IL_0045
IL_0006: ldarg.0
IL_0007: call System.String get_StringProperty()/QuickSearchTests.Entities.BasicClass
IL_000c: dup
IL_000d: brfalse IL_0022
IL_0012: ldarg.1
IL_0013: call Boolean ContainsIgnoreNull(System.String, System.String)/MyCompany.MyProduct.Common.Extensions.StringExtensions
IL_0018: brtrue IL_0047
IL_001d: br IL_0023
IL_0022: pop
IL_0023: ldarg.2
IL_0024: brfalse IL_0045
IL_0029: ldarg.0
IL_002a: call Int32 get_IntProperty()/QuickSearchTests.Entities.BasicClass
IL_002f: call System.String ToString()/System.Int32
IL_0034: ldarg.1
IL_0035: call Boolean ContainsIgnoreNull(System.String, System.String)/MyCompany.MyProduct.Common.Extensions.StringExtensions
IL_003a: brtrue IL_0047
IL_003f: br IL_0045
IL_0044: pop
IL_0045: ldc.i4.0
IL_0046: ret
IL_0047: ldc.i4.1
IL_0048: ret
For reference, here is the definition of BasicClass
public class BasicClass
{
public string StringProperty { get; set; }
public int IntProperty { get; set; }
internal string InternalStringProperty { get; set; }
}
You are calling a method on a non object: the integer value needs to be boxed in order to have the method invocation work. I would patch your code this way (Z is your choice):
.locals
[Z] int32 temp
callvirt instance int32 QuickSearchTests.Entities.BasicClass/BasicClass::get_IntProperty()
stloc.Z
ldloca.s temp
call instance string [System.Runtime]System.Int32::ToString()
A word of warning: writing IL directly is something particularly challenging. You must be aware that once your IL code works as you expect, you have not finished yet, as you should verify it with peverify. Valid IL is not automatically verifiable.
For this reason, as suggested in the comment, the best approach is to study the documentation and learn from a very special teacher: the compiler. Start by writing your code in a supported language and learn how the compiler turns into IL.

When tampering an assembly, why can't I remove original instructions?

In order to be able to test legacy code which relies on SharePoint, I need to mock some of the objects of SharePoint. I do this by tampering with SharePoint assemblies, replacing their methods by mine on the fly.
This works for some cases, but not for others. A strange situation I encountered is this one.
I want to replace the getter of SPContext.Current by my own implementation; for the sake of simplicity, my implementation just throws an exception:
.property class Microsoft.SharePoint.SPContext Current()
{
.get class Microsoft.SharePoint.SPContext Proxy.SPContextProxy::get_Current()
}
.method public hidebysig specialname static
class Microsoft.SharePoint.SPContext get_Current () cil managed
{
// Method begins at RVA 0x877e68
// Code size 12 (0xc)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Proxy don't have an effective implementation of this property."
IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
IL_000b: throw
} // end of method SPContextProxy::get_Current
When tampering the original assembly, if I replace the IL code corresponding to SPContext.Current getter, the property cannot be used any longer. I can't even visualize its contents in ILSpy, because this is what is shown instead:
System.NullReferenceException: Object reference not set to an instance of an object.
at Mono.Cecil.Cil.CodeReader.ReadExceptionHandlers(Int32 count, Func`1 read_entry, Func`1 read_length)
at Mono.Cecil.Cil.CodeReader.ReadSection()
at Mono.Cecil.Cil.CodeReader.ReadFatMethod()
at Mono.Cecil.Cil.CodeReader.ReadMethodBody()
at Mono.Cecil.Cil.CodeReader.ReadMethodBody(MethodDefinition method)
at Mono.Cecil.MethodDefinition.<get_Body>b__2(MethodDefinition method, MetadataReader reader)
at Mono.Cecil.ModuleDefinition.Read[TItem,TRet](TRet& variable, TItem item, Func`3 read)
at Mono.Cecil.MethodDefinition.get_Body()
at ICSharpCode.Decompiler.Disassembler.ReflectionDisassembler.DisassembleMethodInternal(MethodDefinition method)
at ICSharpCode.ILSpy.ILLanguage.DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.DecompileNodes(DecompilationContext context, ITextOutput textOutput)
at ICSharpCode.ILSpy.TextView.DecompilerTextView.<>c__DisplayClass16.<DecompileAsync>b__15()
On the other hand, when I insert my instructions before the original instructions, I can call the getter successfully, as well as see its contents in ILSpy:
.property class Microsoft.SharePoint.SPContext Current()
{
.custom instance void [Microsoft.SharePoint.Client.ServerRuntime]Microsoft.SharePoint.Client.ClientCallableAttribute::.ctor() = (
01 00 00 00
)
.get class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::get_Current()
}
.method public hidebysig specialname static
class Microsoft.SharePoint.SPContext get_Current () cil managed
{
// Method begins at RVA 0x33e2d8
// Code size 61 (0x3d)
.maxstack 1
.locals init (
[0] class Microsoft.SharePoint.SPContext,
[1] class [System.Web]System.Web.HttpContext,
[2] class Microsoft.SharePoint.SPContext
)
... follows by the instructions that I inserted:
IL_0000: nop
IL_0001: ldstr "Proxy doesn't implement this property yet."
IL_0006: newobj instance void [mscorlib]System.NotImplementedException::.ctor(string)
IL_000b: throw
... follows by the original instructions:
IL_000c: ldnull
IL_000d: stloc.0
IL_000e: call class [System.Web]System.Web.HttpContext [System.Web]System.Web.HttpContext::get_Current()
IL_0013: stloc.1
IL_0014: ldloc.1
IL_0015: brfalse.s IL_0039
.try
{
IL_0017: ldloc.1
IL_0018: call class Microsoft.SharePoint.SPWeb Microsoft.SharePoint.WebControls.SPControl::GetContextWeb(class [System.Web]System.Web.HttpContext)
IL_001d: brtrue.s IL_0023
IL_001f: ldnull
IL_0020: stloc.2
IL_0021: leave.s IL_003b
IL_0023: leave.s IL_002a
} // end .try
catch [mscorlib]System.InvalidOperationException
{
IL_0025: pop
IL_0026: ldnull
IL_0027: stloc.2
IL_0028: leave.s IL_003b
} // end handler
.try
{
IL_002a: ldloc.1
IL_002b: call class Microsoft.SharePoint.SPContext Microsoft.SharePoint.SPContext::GetContext(class [System.Web]System.Web.HttpContext)
IL_0030: stloc.0
IL_0031: leave.s IL_0039
} // end .try
catch [mscorlib]System.IO.FileNotFoundException
{
IL_0033: pop
IL_0034: leave.s IL_0039
} // end handler
catch [mscorlib]System.InvalidOperationException
{
IL_0036: pop
IL_0037: leave.s IL_0039
} // end handler
IL_0039: ldloc.0
IL_003a: ret
IL_003b: ldloc.2
IL_003c: ret
} // end of method SPContext::get_Current
What prevents the code from being loaded by ILSpy when original instructions are removed before new ones are inserted?
Notes:
Tampering is done with Mono.Cecil by using MethodDefinition.Body.Instructions collection (and the corresponding Insert and Remove methods.)
A few other methods and properties of Microsoft.SharePoint assembly are tampered successfully: ILSpy displays the resulting IL code.
I thought that .maxstack directive could be a problem (1 in the original property, 8 in the proxied one, 1 in the result). After a few tests on a separate project, it appears that it has no effect.
I also suspected that exceptions could be the cause (the original code throws different exceptions than the new one). After a few tests on a separate project, it appears that it has no effect either.
When IL is shown in textual form, exception handling blocks (.try, catch, etc.) appear as actual blocks of IL instructions, just like they do in C#.
But in the binary form, exception handling blocks are stored separately (see §II.25.4.6 Exception handling clauses of ECMA-335) and reference the IL instructions using offsets. In Cecil, exception handlers are represented using the MethodBody.ExceptionHandlers property.
So, if you replaced the old MethodBody.Instructions with your own instructions, it's very likely that the offsets of the old exception handlers are now invalid, which is causing the issues. (The fact that Cecil throws NullReferenceException sounds like a bug to me, consider reporting it.)
The other example that you linked to which doesn't exhibit this problem is different because there the original method doesn't contain exception handlers, it throws an exception. And throw is just a normal IL instruction, it doesn't have a special representation like e.g. .try/catch does.

VB.net inheritance, Member not found on only one call

I'm at a loss on this one. I have a class that inherits from "BaseClass". BaseClass has a protected member called "UpdateData".
I have a class "DataAccess" that inherits from "UpdateData". DataAccess has many different calls that use the "UpdateData" method. This works just fine except for one single method on "DataAccess" that returns a "Member Not found" exception.
I've cleaned and rebuilt, and it's happening in Visual Studio as well as when deployed to the server. The call to 'Add' works, but the call to 'Add Detail' is failing.
I've tried removing the transaction scopes listed below, tried using a factory pattern to get a new instance for each call, but neither has worked. I'd rather not open the method up as "public" as we have hundreds of classes that use the protected "UpdateData" method just fine?
Any help, or new ideas are greatly appreciated!
''''On the base class
Protected Sub UpdateData(ByVal connString As String, ByVal procName As String, ByVal ParamArray params As Object())
UpdateDataWithTimeout(connString, procName, getTimeoutSetting, params)
End Sub
''''On the inherited class. This one works
Protected Friend Overridable Sub Add(ByVal s1 As String, _
ByRef s2 As String, _
ByVal s3 As String, _
ByVal params As List(Of Object))
Try
params.Insert(0, s1)
params.Insert(1, s2)
params.Insert(2, s3)
UpdateData(DB, SPI_PROC1, params.ToArray())
Catch ex As Exception
Throw ex
End Try
End Sub
''''On the inherited "DataAccess" class. This one fails.
Protected Friend Overridable Sub AddDetail(ByVal ParamArray parms As Object())
If Condition1 Then
UpdateData(_sysConn, SPI_PROC, parms)
End If
End Sub
''''This is from a method in the class calling the "DataAccess" code
Dim da As DataAccess
da = New DataAccess(strVariable)
Using scope As New TransactionScope(TransactionScopeOption.RequiresNew)
''''This call works just fine and can find "UpdateData"
da.Add(string1, string2, string3, objectParameterList)
'Separate database, so avoid MSDTC with new transaction scope
Using scope2 As New TransactionScope(TransactionScopeOption.RequiresNew)
''''This call fails to find UpdateData
da.AddDetail(string1, string5, string6, string7, string8, string3)
scope2.Complete()
scope.Complete()
End Using
End Using
Addendum: There is a difference in the IL here with one being "late binding" (which I need to stop) but am not sure how?
Failing method....
IL_001f: ldstr "UpdateData"
IL_0024: ldc.i4.3
IL_0025: newarr [mscorlib]System.Object
IL_002a: stloc.1
IL_002b: ldloc.1
IL_002c: ldc.i4.0
IL_002d: ldarg.0
IL_002e: ldfld string DataAccess::_sysConn
IL_0033: stelem.ref
IL_0034: ldloc.1
IL_0035: ldc.i4.1
IL_0036: ldloc.0
IL_0037: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
IL_003c: stelem.ref
IL_003d: ldloc.1
IL_003e: ldc.i4.2
IL_003f: ldarg.1
IL_0040: stelem.ref
IL_0041: ldloc.1
IL_0042: stloc.2
IL_0043: ldloc.2
IL_0044: ldnull
IL_0045: ldnull
IL_0046: ldc.i4.3
IL_0047: newarr [mscorlib]System.Boolean
IL_004c: stloc.3
IL_004d: ldloc.3
IL_004e: ldc.i4.0
IL_004f: ldc.i4.1
IL_0050: stelem.i1
IL_0051: ldloc.3
IL_0052: ldc.i4.1
IL_0053: ldc.i4.1
IL_0054: stelem.i1
IL_0055: ldloc.3
IL_0056: ldc.i4.2
IL_0057: ldc.i4.1
IL_0058: stelem.i1
IL_0059: ldloc.3
IL_005a: ldc.i4.1
IL_005b: call object [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.NewLateBinding::LateCall(object,
class [mscorlib]System.Type,
string,
object[],
string[],
class [mscorlib]System.Type[],
bool[],
bool)
IL_0060: pop
IL_0061: ldloc.3
IL_0062: ldc.i4.0
IL_0063: ldelem.i1
IL_0064: brfalse.s IL_0088
IL_0066: ldarg.0
IL_0067: ldloc.2
IL_0068: ldc.i4.0
IL_0069: ldelem.ref
IL_006a: call object [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::GetObjectValue(object)
From the working method, here is the IL.
IL_003b: ldarg.0
IL_003c: ldstr "conn"
IL_0041: ldstr "proc"
IL_0046: ldarg.0
IL_0047: callvirt instance string DataAccess::get_SYSCODE_SPAPPEND()
IL_004c: call string [mscorlib]System.String::Concat(string,
string)
IL_0051: ldarg.s params
IL_0053: callvirt instance !0[] class [mscorlib]System.Collections.Generic.List`1<object>::ToArray()
IL_0058: callvirt instance void [BaseClass]BaseClass::UpdateData(string,
string,
object[])
Of course, the moment you ask the question the answer comes to you.
I altered my string variables for the SPI_PROC and SPI_PROC1 to be Private constants on the class rather than variables... set the _sysConn and DB in the class constructor and it switched everything to early binding.
So problem solved. Hopefully this will save someone else a few hours of time.

What's __PostIncrement in decompiled VB (by ILSpy)

I was just perusing the decompiled code for System.Collections.Generic.List(Of T).Add(item As T) using ILSpy and I found a call to __PostIncrement. I've never heard of such a thing in VB, so I did some digging and found:
In VB, the code is Me._items(__PostIncrement(Me._size)) = item.
In C#, the code is this._items[this._size++] = item; using the actual post-increment operator in that language
In MSIL, there is not a function call. It appears to work like a C# post-increment would (the comments are mine, but I'm no expert at MSIL,so I could be wrong).
The code is:
IL_001e: ldarg.0
IL_001f: ldfld !0[] class System.Collections.Generic.List`1<!T>::_items
IL_0024: ldarg.0
IL_0025: dup
IL_0026: ldfld int32 class System.Collections.Generic.List`1<!T>::_size
IL_002b: dup
IL_002c: stloc.0 // store the size pre incrementing
IL_002d: ldc.i4.1
IL_002e: add // do the increment
IL_002f: stfld int32 class System.Collections.Generic.List`1<!T>::_size
IL_0034: ldloc.0 // reload the stored size to use as index in stelem
IL_0035: ldarg.1
IL_0036: stelem.any !T
What exactly is this __PostIncrement? Is it a SharpDevelop invention to symbolize post-increment IL code in VB? Or is it actually some kind of definition I could use in my own VB code?
__PostIncrement is the equivalent of the C# operator++. It is a quick way to increment (add one) to a number. Unfortunately, according to the list of operators in Visual Basic, there is no equivalent.

Run-Time Check Failure #0 vb.net callback from C dll

I'm writing Add-inn Application A in VB.Net and DLL B in C language.
Application A pass callback method to dll B.
When certain event occur the dll invoke the callback from A.
Whole works fine on my PC but when I move it to Notebook I get an error:
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.
This is part of C code:
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
DWORD dwParam1,
DWORD dwParam2,
DWORD dwParam3){
switch (dwMessage)
{
case LINE_CALLSTATE:
switch (dwParam1)
{
case LINECALLSTATE_OFFERING:
if(dwInstance!=NULL)
{
try
{
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
}
catch(...)
{
MessageBox (NULL, L"( (OFFICE_PTR)dwInstance )(&sCallNr)",L"ERROR",MB_OK);
}
}
break;
};
break;
}
}
Where dwInstance is a address of application A callback method
This is part of VB.Net code:
Public Class TapiPlugin
Public Delegate Sub P_Fun()
Private Declare Function startSpy _
Lib "TAPIClient.dll" _
(ByVal pFun As P_Fun) As IntPtr
Public Shared Sub simpleTest()
MsgBox("Plugin sub simpleTest")
End Sub
Public Sub onStart()
Dim pBSTR As IntPtr
pBSTR = startSpy(AddressOf simpleTest)
MsgBox(Marshal.PtrToStringAuto(pBSTR))
Marshal.FreeBSTR(pBSTR)
End Sub
End Class
The Error occur when I try call 'vbFunc( )'. I would be grateful for any help. :D
If the calling convention is cdecl, then you need to declare your delegate like this:
<UnmanagedFunctionPointer(CallingConvention.Cdecl)>
Public Delegate Sub P_Fun()
You can only do this in .NET 2.0 and after, as the attribute was not introduced before then (and the interop layer was not changed to acknowledge it before that).
If the calling convention is indeed stdcall then the delegate can remain as is. You said it is stdcall, but I have doubts, since the exception is explicitly telling you that there might be a mismatch in calling conventions.
Do the two computers have different pointer sizes perhaps? Maybe one is a 64 bit machine and the other only 32?
typedef void (__cdecl * OFFICE_PTR)();
void TAPIClient::tapiCallBack(
DWORD hDevice,
DWORD dwMessage,
DWORD dwInstance,
...){
...
OFFICE_PTR vbFunc =(OFFICE_PTR)dwInstance;
vbFunc( );//Critical moment
The DWORD type is not really valid for passing pointer types. You should be using INT_PTR I guess.
I thing it is not a reason to check it out I passed the callback as global pointer of type OFFICE_PTR and i get the same result. On PC it work fine on Notebook it crash :(
A have to apologies for a mistake I wrote that the def look like:
typedef void (__cdecl * OFFICE_PTR)();
but for real it looks like
typedef void (__stdcall * OFFICE_PTR)();