Fixing PDFSharp hangs

January 12, 2017

To analyse a couple of PDF files whether they contain only images, I used the latest release build of PDFsharp, version 1.32.

However, when processing a certain file (of unknown origin) using code found in an SO answer

public static IEnumerable ExtractText(this PdfPage page)
{       
    var content = ContentReader.ReadContent(page);      
    var text = content.ExtractText();
    return text;
}   

the ExtractText() function simply would not return.

I upgraded to the most current build 1.50 beta 3, included the source in my project, and ran it in Debug mode, where execution halted in the file PDFsharp\src\PdfSharp\Pdf.Content\CParser.cs line 163 failing an assertion:

#if DEBUG
    default:
        Debug.Assert(false);
        break;
#endif

Without digging too deep into the analysis of PDF files, it was clear that the PDF contained a CSymbol that is not being handled by the library, and thus (most likely) ended up in an infinite loop inside CParser.ParseObject().

I fixed this by replacing the Debug.Assert statement with

        throw new Exception("unhandled PDF symbol " + symbol);

which fixed the situation for me.

Advertisements

Software Inventory: Firefox Extensions

October 24, 2016

Privacy

FoxyProxy Standard

Ghostery

Hide My IP

Content

Block site

Flashblock

Video DownloadHelper

Bookmark Management

Go Parent Folder

Show Parent Folder

Development

Quick Locale Switcher

Most of the functionality of previous developer’s extensions Firebug and Web Developer seems to be included in standard Firefox.

Screenshots

Screengrab (fix version)

Session Management

Session Manager

 


Retrieving the List of Installed Programs in Windows 7

October 14, 2016

The Programs and Features view in the Windows Control Panel does not provide a means to export the list of installed programs as a text file.

A quick web search brought up the information that the information about installed software resides under the registry key

HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall

or, on 64-bit systems, under

HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall

(Source: social technet, Scripting Guy, HowToGeek, SuperUser)

PowerShell kann enumerate the information under these keys using the Get-ItemProperty commandlet. You can retrieve the PS properties of this data using the Get-Member commandlet.

So the first PS command to retrieve this list is

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*

However, Control Panel does not display the registry entry if

  • its DisplayName is empty or not set
  • its UninstallString is empty or not set

which results in the command

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | 
where-object {$_.DisplayName -and $_.UninstallString }

To retrieve the programs directly in PowerShell ISE, we can simply use the Format-Table commandlet

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | 
Where-Object {$_.DisplayName -and $_.UninstallString } | 
Select-Object Publisher, DisplayName, DisplayVersion, InstallLocation, InstallDate, URLInfoAbout, UninstallString | 
Sort-Object Publisher,DisplayName | 
Format-Table

To output to a file for further processing in Excel, export to a .csv file like this

Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* | 
Where-Object {$_.DisplayName -and $_.UninstallString } | 
Select-Object Publisher, DisplayName, DisplayVersion, InstallLocation, InstallDate, URLInfoAbout, UninstallString | 
Sort-Object Publisher,DisplayName | 
Export-Ssv -delimiter "`t" -encoding "unicode" c:\temp\installed.csv

Software Inventory: Chrome Extensions

October 13, 2016

Screenshot

Awesome Screenshot

FireShot

Full Page Screen Capture

Developer Tools

J CSS Reload

Page load time

Quick Javascript Switcher

Window Resizer

Viewport Dimensions

Tabs and Sessions

Session Buddy

Tab Ahead

Tab-Snap

The Great Suspender

Other

Google Translator

Ghostery


Finding Deleted IIS Application Directories

October 13, 2016

If you develop web applications for IIS, or administrate IIS and file systems, you sooner or later end up with orphaned IIS application which point to non-existing directories.

To get an overview of IIS applications, start up PowerShell in administrator mode (I prefer PowerShell ISE) and run

import-module webadministration
dir "IIS:\\sites\Default Web Site"

To exclude any files that reside in IIS root, filter out the “file” entries:

import-module webadministration
dir "IIS:\\sites\Default Web Site" | where { $_.NodeType.Trim() -ne "file" }

Finally, we test the Physical Path property of the resulting entries

import-module webadministration
dir "IIS:\\sites\Default Web Site" 
    | where { $_.NodeType.Trim() -ne "file" 
        -and ![System.IO.Directory]::Exists( $_.PhysicalPath ) }

This command lists all IIS web application and virtual root directories which point to non-existing directories. The result looks like this:

Type        Name         Physical Path 
----        ----         ------------- 
application application1 D:\app\app1
application web2         D:\app\web2

Installing the HighlightJS Plugin in Redmine 3.3

July 19, 2016

We wanted to document VBScript code in a Redmine wiki, which ships with a syntax highlighter called CodeRay. There is no VBScript mode in CodeRay, but it can easily be added by copying an existing Scanner and adding and removing some code 😉

Unfortunately CodeRay seems to have a problem with symbols with leading ampersands, as documented in this Redmine issue, and VBScript uses &H to indicate hex numbers.

Search brought up the HighlightJS Plugin as an alternative, which I downloaded and copied into the plugins directory.

Unfortunately, Redmine would not start up again.

Looking for the log files, hoping for an indication what goes wrong, I found the file process.log with the following data:

Started GET "/redmine/admin/plugins" for 127.0.0.1 at 2016-07-13 11:46:57 +0200
Processing by AdminController#plugins as HTML
Current user: redadmin (id=1)
Rendered admin/plugins.html.erb within layouts/admin (15.6ms)
Rendered admin/_menu.html.erb (15.6ms)
Rendered layouts/base.html.erb (421.9ms)
Completed 500 Internal Server Error in 625ms (ActiveRecord: 62.5ms)

ActionView::Template::Error (uninitialized constant Highlightjs::Hooks::ViewHighlightedHook::UserAgent):
13: <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
14: <%= javascript_heads %>
15: <%= heads_for_theme %>
16: <%= call_hook :view_layouts_base_html_head %>
17: <!-- page specific tags -->
18: <%= yield :header_tags -%>
19: </head>
plugins/redmine_highlightjs/lib/hooks/view_highlighted_hook.rb:15:in `view_layouts_base_html_head'
lib/redmine/hook.rb:61:in `block (2 levels) in call_hook'
lib/redmine/hook.rb:61:in `each'
lib/redmine/hook.rb:61:in `block in call_hook'
lib/redmine/hook.rb:58:in `tap'
lib/redmine/hook.rb:58:in `call_hook'
lib/redmine/hook.rb:96:in `call_hook'
app/views/layouts/base.html.erb:16:in `_app_views_layouts_base_html_erb___698415948_43865832'
app/views/layouts/admin.html.erb:8:in `_app_views_layouts_admin_html_erb___707869534_44477868'
lib/redmine/sudo_mode.rb:63:in `sudo_mode'

Now I have no idea how Ruby works, or Redmine, and how to deal with packages and so on. Experimenting with the gem package manager did not fix the situation either.

What I did find though was the location that raised the error, namely checking whether the browser name and version retrieved from the HTTP UserAgent string was among the “supported” browsers. Since it’s hard to imagine that any current browser does not support the highlighting magic of this plugin, I simply deactivated the check in the file view_highlighted_hook.rb:

#user_agent = UserAgent.parse(request)
user_agent = 'MyBrowser'     # don't retrieve, this var is used for logging later

if true            # SupportedBrowsers.detect { |browser| user_agent >= browser }

 


TypeScript logging with log4javascript in an ASP.Net MVC application

July 12, 2016

I needed to add logging functionality to an existing TypeScript application hosted on ASP.Net MVC.

Since I usually use log4net in C#, log4javascript was an obvious choice, as it provides a similar API.

First, I needed to declare the parts of the log4javascript API I use in TypeScript:

declare module log4javascript {
    class Logger {
        info(msg: string);
        info(msg: string, value: any);
        addAppender(app: AjaxAppender);
    }
    function getLogger(name: string): Logger;
    function getNullLogger(): Logger;
    class AjaxAppender {
        constructor(url: string);
        setSessionId(id: string);
        setLayout(layout: JsonLayout);
        addHeader(key: string, value: string);
    }
    class JsonLayout {
        constructor(readable: boolean, combine: boolean);
    }
}

In the logging class, I added logger and AjaxAppender. Depending on the data to log, I also configure a JsonLayout.

logger: log4javascript.Logger;

A non-logging logger instance is created using getNullLogger():

this.logger = log4javascript.getNullLogger();

Plain-text AjaxAppender

Use these statements to create an Ajax-based logger in TypeScript:

this.logger = log4javascript.getLogger("MyLoggerName");
var app = new log4javascript.AjaxAppender("http://myhost/myapp/mylogger");
this.logger.addAppender(app);

On the MVC side, the controller receives the parameters: logger, timestamp, level, url, message, sessionid, and layout. (Note that these parameter names can be changed using the setKeys() method)

The value for sessionid is set using

ajaxAppender.setSessionId(sessionId);

To log a string message, simply write

this.logger.info("my info log message");

The C# controller method stub therefore looks something like this:

[ValidateInput(false)]
[HttpPost]
public ActionResult Log(long? timestamp, string level, string sessionid, string message)
{
    var ipaddr = Request.ServerVariables["HTTP_X_FORWARDED_FOR"];
    if (string.IsNullOrEmpty(ipaddr))
        ipaddr = Request.ServerVariables["REMOTE_ADDR"];
    if (string.IsNullOrEmpty(ipaddr))
        ipaddr = Request.UserHostAddress;

    if (level == "INFO")
        logger.Info(ipaddr + " " + sessionid + " " + message);
    else
        logger.Debug("[" + level + "] " + ipaddr + " " + sessionid + " " + message);
    return Json(null);
}

where logger is an instance of log4net.Ilog.

JSON-formatted AjaxAppender

To change the appender’s data format to JSON, we set its layout to JsonLayout:

this.logger = log4javascript.getLogger("MyJsonLogger");
var app = new log4javascript.AjaxAppender("http://myhost/myapp/myjsonlogger");
app.setLayout(new log4javascript.JsonLayout(false, false));
app.addHeader("Content-Type", "application/json");
this.logger.addAppender(app);

The C# controller method receives a JSON array of log entries

    [
        {
            "logger":"MyJsonLogger",
            "timestamp":1448621329758,
            "level":"INFO",
            "url":"http://localhost/myapp/...",
            "message":
            [    "message1",
                "message2", ...
            ]
        }
    ]

where message1, message2, etc are the parameters of the logger’s debug/info/warn etc. calls.

We create a C# class containing the required properties

public class Data
{
    public string logger;
    public long? timestamp;
    public string level;
    public string url;
    public string[] message;
}

and deserialize the HTTP request’s InputStream using JSON.Net:

[ValidateInput(false)]
[HttpPost]
public ActionResult Execute()
{
    string inputContent;
    using (var sr = new StreamReader(HttpContext.Request.InputStream))
    {
        inputContent = sr.ReadToEnd();
    }
    var data = JsonConvert.DeserializeObject<Data[]>(inputContent);

    if (data != null)
    {
        foreach (var d in data)
        {
            if (d.level == "INFO")
                logger.Info(d.message[0] + " " + d.message[1]);
            else
                logger.Debug("[" + d.level + "] " + d.message[0] + " " + d.message[1]);
        }
    }
    return Json(null);
}

Again, logger is an instance of log4net.Ilog.