27 Şubat 2020 Perşembe

ILSpy - Decompiling .Net Core self-contained single file exe

Using ILSpy, if you try to decompile a .net core single-file self-contained exe, you will get an error like:

PE file does not contain any managed metadata.




Googling the issue, I found that this is because the single-file exe is an unmanaged wrapper.  ILSpy (as of version 6.0.0) does not support decompiling this.

But I found a way...

I opened the exe using windbg:
- Launch windbg.
- Select File > Open executable...
- Select the exe.

It will launch the exe and attach itself as a debugger.  You should be able to see the assemblies being loaded.  Initially, windbg will break immediately. Use the 'g' command so that it will continue execution of the program. It should continue and load more assemblies.

Here comes the interesting part...











































The .net core assembly that I'm trying to decompile is 'ConsoleApp11.dll' and it looks like it's being loaded from a temp folder.  Apparently, the wrapper exe unwraps its contents to a temp folder and executes the .net core dll from that location.  So, can I go to that temp folder and decompile the unwrapped dlls?  Looks like, Yes!

When I take a look at that location, I see my dll:




And now, I can use ILSpy to decompile that dll:



I was initially using windbg here, because I needed more details when trying to understand what the exe was doing. Now that I know how the single-file exe works, I don't need to use windbg in the future if I just need to know the location of the temp folder.  I could use any other app that shows the assemblies loaded by an exe, like SysInternals Process Explorer (procexp) or Process Monitor (procmon).

16 Mart 2018 Cuma

Tip: WPF - How to hide elements in Visual Studio at Design-time

Occationally, when using the WPF xaml designer in Visual Studio, we have some UI elements that we would like to hide from the design view.  Examples of these might be some error messages, busy indicators, icons, etc.



Fortunately, we can do this pretty easily by setting d:IsHidden=true in xaml:





22 Şubat 2018 Perşembe

Solved: "The resource "{x:Type common:MyCustomControl}" could not be resolved."

I have a project where multiple WPF applications share a "common" library.  This library includes some common WPF resources, utility methods, etc.

I ran into a problem when I decided to move a custom control from the apps into the common library. I moved the custom control definition (MyCustomControl.cs) and the Theme/Generic.xaml, which contains the default style / template for my control.

In one of the applications, I was trying to further customize the default style by creating a style based on the default style:

<Style TargetType="common:MyCustomControl" BasedOn="{StaticResource {x:Type common:MyCustomControl}}">

   ...

</Style>
The StaticResource had an error that said "The resource "{x:Type common:MyCustomControl}" could not be resolved."

Solution:

It turned out I forgot to add the ThemeInfo assembly attribute, which defines where the themes are stored in the assembly.  When I added the following in the AssemblyInfo.cs in my common library, the problem was solved:

[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]


18 Şubat 2018 Pazar

Serialization of DateTime and DateTimeOffset in Json.NET

Deserializing string to DateTimeOffset

Json.NET uses the ISO 8601 format for representing the datetime strings, which is in the format of yyyy-MM-ddTHH:mm:ss.fffffffzzz. To dissect this format a little bit: 

yyyy: 4 digit years.
MM : 2 digit months. 
dd    : 2 digit days
"T"  : a literal that separates the "date" section from the "time" section.
HH  : 2 digit hours in the 24 hour format. 
mm  : 2 digit minutes
ss     : 2 digit seconds
fffffff: 7 digit fraction of seconds
zzz    :  offset from UTC. This can be in the format of -/+hh:mm (e.g. +02:00, 2 hours ahead of UTC) or it can be the literal "Z" which is the same thing as UTC (+00:00).

Examples would be:

"2000-01-01T12:34:56+02:00"
"2000-01-01T12:34+01:30"
"2000-01-01T12:34:56Z"
"2000-01-01 12:34:56"
"2000-01-01 12:34"
"2000-01-01"
"12:34:56+02:00"
"12:34:56Z"
"12:34:56"
"12:34"

As you can see, some parts of the datetime string can be omitted. and here are a few things to note about that:
  • When the date part is omitted altogether, Json.NET will then assume the date is today
  • When the time part is omitted, Json.NET will assume the time is 00:00:00 (midnight).
  • The literal "T" can be replaced with one or more spaces (generally a single space).
  • To state the obvious, the fraction of seconds can be omitted and will be assumed as zero.
  • When the UTC offset is omitted, it will be assumed as "local". That means Json.NET will use the current system timezone to calculate the UTC offset at the specified datetime. 

    Note that this is NOT necessarily same thing as the UTC offset today.  For example, deserializing "2000-01-01 11:22:33" will use the UTC offset of the local timezone on 2000-01-01 as opposed to today.  For Pacific Time Zone, the UTC offset on January 1st, 2000 was -08:00 hours. For the same time zone, the UTC offset on July 1st, 2000 was -07:00 hours. 

Deserializing string to DateTime

Almost all of above is also correct when deserializing a string to DateTme. However, since DateTime cannot express the UTC offset, the offset is pretty much ignored, except for setting the DateTime.Kind property:
  • If the offset is included in the form of +/-hh:mm, the DateTime.Kind is set to "Local":
    "2000-07-01T12:34+03:00" -->  DateTimeKind.Local
  • If the offset is specified as UTC using the literal "Z", the DateTime.Kind is set to "Utc":
    "2000-07-01T12:34Z"  --> DateTimeKind.Utc
  • If the offset is omitted, the DateTime.Kind is set to "Unspecified":
    "2000-07-01T12:34"  --> DateTimeKind.Unspecified

Serializing DateTimeOffset to string

This is pretty straightforward. When serializing a DateTimeOffset, it always produces a string in the ISO 8601 format we mentioned above.  Maybe the only thing to note here, the fraction of seconds will not be in the string, unless it's greater than zero. 

Serializing DateTime to string

Same as DateTimeOffset, serializing a DateTime will produce a string in the ISO 8601 format above. However, the presentation of the offset depends on the DateTime.Kind property:
  • DateTimeKind.Local will produce a string with the offset set to the local timezone's UTC offset at the specified time. e.g. new DateTime(2000,1,1,0,0,0,DateTimeKind.Local) will be serialized with the UTC offset of the local timezone on 2000-01-01.
  • DateTimeKind.Utc will produce a string with the literal "Z" as the offset.
  • DateTimeKind.Unspecified will produce a string with no offset specified. 


18 Aralık 2011 Pazar

WPF Busy Indicator

I wanted to create a spinning busy indicator like the one consistently used accross the iPhone UI.  Inspired by this blog, I accomplished this by spinning a png image, like the one below, using the WPF rotatetransform and storyboard.
 
I created a user control called BusyIndicator that uses a white color version of this image in order to show it on a darker UI surface.  BusyIndicator also has Text dependency property that can optionally display a description for the work being done.  Here's how it looks on a login screen (which happens to be in Turkish). 


The user control is almost entirely written in XAML.  I wrote C# code only for the TextToVisibility converter, which is used for the binding that binds the text label's visibility to length > 0 condition of the Text property, and for defining the Text dependency property of course.

XAML:
<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:UIPack" mc:Ignorable="d"
    x:Class="UIPack.BusyIndicatorControl"
    x:Name="UserControl" IsEnabled="False">
    <UserControl.Resources>
       
        <local:TextToVisibilityConverter x:Key="TextToVisibilityConverter"/>
       
        <Style x:Key="BusyImage" TargetType="{x:Type Image}">
            <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
            <Setter Property="RenderTransform">
                <Setter.Value>
                    <RotateTransform Angle="0"/>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="true">
                    <Trigger.EnterActions>
                        <BeginStoryboard>
                            <Storyboard Timeline.DesiredFrameRate="12">
                                <DoubleAnimation
                                    Storyboard.TargetProperty="RenderTransform.Angle"
                                    From="0" To="360" Duration="0:0:1"
                                    RepeatBehavior="Forever" />
                            </Storyboard>
                        </BeginStoryboard>
                    </Trigger.EnterActions>
                </Trigger>
            </Style.Triggers>
        </Style>
       
        <Style x:Key="MyStackPanelStyle" TargetType="{x:Type StackPanel}">
            <Style.Resources>
                <Storyboard x:Key="Show">
                    <ObjectAnimationUsingKeyFrames
                        Storyboard.TargetProperty="(UIElement.Visibility)"
                        Storyboard.TargetName="{x:Null}">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0.3"
                            Value="{x:Static Visibility.Visible}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
                <Storyboard x:Key="Hide">
                    <ObjectAnimationUsingKeyFrames
                        Storyboard.TargetProperty="(UIElement.Visibility)"
                        Storyboard.TargetName="{x:Null}">
                        <DiscreteObjectKeyFrame KeyTime="0:0:0"
                            Value="{x:Static Visibility.Collapsed}"/>
                    </ObjectAnimationUsingKeyFrames>
                </Storyboard>
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="True">
                    <Trigger.ExitActions>
                        <BeginStoryboard Storyboard="{StaticResource Hide}"/>
                    </Trigger.ExitActions>
                    <Trigger.EnterActions>
                        <BeginStoryboard Storyboard="{StaticResource Show}"/>
                    </Trigger.EnterActions>
                </Trigger>
            </Style.Triggers>
        </Style>
       
    </UserControl.Resources>

    <Grid x:Name="LayoutRoot">
        <StackPanel Orientation="Horizontal"
            IsEnabled="{Binding IsEnabled, ElementName=UserControl}"
            Style="{DynamicResource MyStackPanelStyle}"
            Visibility="Collapsed">
            <Image Source="busy.png"
                Style="{DynamicResource BusyImage}"
                MaxWidth="50" MaxHeight="50"/>
            <TextBlock x:Name="textBlock" TextWrapping="Wrap" Margin="5,0,0,0"
                Foreground="{Binding Foreground, ElementName=UserControl, Mode=OneWay}"
                Text="{Binding Text, ElementName=UserControl, Mode=OneWay}"
                Visibility="{Binding Text,
                            Converter={StaticResource TextToVisibilityConverter},
                            ElementName=UserControl, Mode=OneWay}"/>
        </StackPanel>
    </Grid>
   
</UserControl>

C# code:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace UIPack
{
    /// <summary>
    /// Interaction logic for BusyIndicatorControl.xaml
    /// </summary>
    public partial class BusyIndicatorControl : UserControl
    {
        public static readonly DependencyProperty TextProperty = DependencyProperty.Register("Text",
                    typeof(string), typeof(BusyIndicatorControl),new FrameworkPropertyMetadata(new PropertyChangedCallback(TextChangedCallback)));


        public string Text
        {
            get { return (string)this.GetValue(TextProperty); }
            set { this.SetValue(TextProperty, value); }
        }

        private static void TextChangedCallback(DependencyObject o, DependencyPropertyChangedEventArgs e)
        {
            var busyIndicator = o as BusyIndicatorControl;
            busyIndicator.textBlock.Text = (string)e.NewValue;
        }

        public BusyIndicatorControl()
        {
            this.InitializeComponent();
        }
    }

    public class TextToVisibilityConverter : IValueConverter
    {
        #region IValueConverter Members

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (value == null || ((string)value).Trim().Length == 0)
                return Visibility.Collapsed;
            else
                return Visibility.Visible;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException(Properties.Resources.NotImplemented);
        }

        #endregion
    }
}

Things to note:
  • Simply spinning the image doesn't give the iPhone like experience. If you look at the iPhone busy indicator, the 12 tick marks on the image always appear at the top-of-the-hour clock positions and never appear in-between.  To give this experience, you'll notice I reduced the WPF storyboard frame rate to 12 fps (Timeline.DesiredFrameRate="12"), which is 60 fps by default.  With the storyboard duration of 1s, this causes each frame of the animation to rotate the image just enough so that the tick marks "jump" to the next clock position.  You can try Timeline.DesiredFrameRate = 60 to see the default behavior.  
  • I used a 50x50 image that scales to the size of the user control.  If you need to use this user control in small sizes (say, height less than 15), you might consider using an image with 6 tick marks instead of 12, and reducing the frame rate to 6 fps. 
  • I show/hide this user control by enabling and disabling it.  Notice that the inner stackpanel has a trigger for becoming visible after a 300 ms delay.  I did this because I only want to show the busy indicator for "long" waits.  I decided; if the action takes 300 ms, it is likely that this is an action that can take a while and we should show a busy indicator.  However, if the action only takes say 10 ms, the busy indicator would merely flash on the UI, which doesn't look good.  For example, in my example above, the user login only takes a few milliseconds *most of the time*, and we don't need to disrupt the UI experience with the busy indicator. In such a short period of time, the human brain won't even comprehend the pause anyway.  However, if there is a network problem, the client might try and fail to connect to the server.  While trying to connect, we should show the busy indicator.