SideBySide or Registration-Free Activation of Vulcan.NET/X# components

May 06
2016

In this post I will try to list some things that are needed for registration-free use of Vulcan.NET or X# components in Visual Objects applications. Some of these issues have costed me several days of tries.

First, a background article from MSDN: How to: Configure .NET Framework-Based COM Components for Registration-Free Activation

Then: a big “Thank You” to Meinhard Schnoor who has helped me a lot not only with this issue, but often also with other issues.

How SxS (how I will call it to shorten) works basically? I’ll try to simplify at maximum what is needed and where the potential problems stay.

Normally, COM works only with “registered” components. This has some issues:

  • you can only register components from your local machine, not from a network share
  • you need administrative rights to register the component
  • the component registration is global for the machine, you cannot use different versions of the component
  • With SxS things are different:

  • components are called from the program directory without installation
  • every application can use the proper component version
  • of course no administration rights are needed
  • Unfortunately, SxS is very bad documented, and there are not many helpful articles and tools available. And if something goes wrong, it may be very difficult to find out the “why”. sxstrace.exe normally is a help, but not every time. Manifest caching can also disturb.

    Manifests

    Manifests are the basic mechanism for SxS, they are needed for both the component and the executable. The manifest for the component needs to specify the available classes and GUIDs, and the manifest for the executable needs to specify the name of the component. At loading time, the loader first reads the manifest for the executable and then the manifest for the component. If something goes wrong, you exe will not start. Look first in the event viewer and then use sxstrace to see the errors. Because of manifest caching it can be necessary to change the time stamp of your executable (best: regenerate it). Manifests can be standalone (name of the exe or dll with added .manifest extension), or built into the executable. My recommendation would be to use standalone manifests for development and embedded manifests for deployment.

    GUIDs

    Every class and also the interfaces need their own GUID – generate a new one with the guidgen tool (You’ll need the Visual Studio Developer Command Prompt for this). Please pay attention to use the right GUIDs!

    Versions

    The entire system is very sensible to versioning! You need to use the correct version in every place: manifests and component itself (an AssemblyInfo.prg with the correct AssemblyVersionAttribute and the AssemblyFileVersionAttribute entries is needed – otherwise SxS will give no errors, but don’t load the component! This alone has costed me several days of tries.

    Naming

    Correct naming is the next potential issue: this is not only important for the name of the DLL and for the name of the classes, but also for the namespace used in the component. Name the component the same as the used namespace (and use a namespace!) to save you troubles (with naming errors the registered assembly may work, the SxS loading will fail silently at runtime).

    Any error in one of the components will cause the failure of the entire loading mechanism.

    Windows service caveats with Process.Start

    Feb 29
    2016

    These days, I was working on a webservice that worked directly with http.sys.
    This service was planned to wait for requests, and launch programs depending on the request.
    Firstly, I planned to use the local system account, and launch the external programs as specific user. This program should be started from a network share, using UNC paths.
    Unfortunately, that does not work, Process.Start cannot do that:
    http://blogs.msdn.com/b/winsdk/archive/2013/11/12/runas-verb-process-start-doesn-t-work-from-a-localsystem-net-service.aspx

    My next decision was to add a user for this purpose (to run the Windows service). Unfortunately, Process.Start ignores the essential settings for a Windows service

    oInfo:UseShellExecute := false
    oInfo:CreateNoWindow := true
    oInfo:ErrorDialog := false
    oInfo:WindowStyle := ProcessWindowStyle.Hidden

    The page from MSDN (https://msdn.microsoft.com/library/0w4h05yb%28v=vs.100%29.aspx) states:
    “If the UserName and Password properties of the StartInfo instance are set, the unmanaged CreateProcessWithLogonW function is called, which starts the process in a new window even if the CreateNoWindow property value is true or the WindowStyle property value is Hidden.”
    At the moment I’m not changing the user from the service, but executing the external program under the service account.

    I had also another small issue: since I was reading the password from an ini file, I thought the StartInfo:PasswordInClearText property was enough. On my development machine it worked, but crashed on the production server, an SBS 2011 (based on Windows Server 2008 R2) because with the .NET Framework 4 this property simply does not exists.

    Another issue: if you plan to start a process as another user from a service: this user needs local logon rights (on a server OS this is a manual change)

    Opening PDF files from network folder with Adobe Reader DC

    Nov 03
    2015

    In some of my software programs, I have integrated a thing like document archiving, and there customers are putting several files, mostly PDF.

    Since Adobe Reader is out and installed on the machines, often PDF files that are on a shared network folder, cannot be opened anymore – Adobe Reader gives an “access denied” error.

    It is the “Protected Mode” of Adobe Reader that gives this problem.

    Fortunately you can disable this mode, and Adobe Reader works again as expected. Please don’t forget to enable the protected mode afterwards!

    This is my VO code for this functionality:

    // disable Adobe Reader protected mode
    aVersions := { “11.0”, “DC” }
    aReset := {}
    nLen := ALen( aVersions )
    for nI := 1 upto nLen
     cVersion := aVersions[nI]
     nMode := RegistryDWord( HKEY_CURRENT_USER, “Software\Adobe\Acrobat Reader\” + cVersion + “\Privileged”, “bProtectedMode” )
     if nMode == 1
      SetRegistryDWord( HKEY_CURRENT_USER, “Software\Adobe\Acrobat Reader\” + cVersion + “\Privileged”, “bProtectedMode”, 0 )
      AAdd( aReset, cVersion )
     endif
    next
    oProcess := SpawnProcess{ cExe, cFileName, SW_NORMAL }
    for nI := 1 upto 20
     ApplicationExec( EXECWHILEEVENT )
     Sleep( 500 ) // let Acrobat reader start
    next
    // re-enable Adobe Reader protected mode
    nLen := ALen( aReset )
    for nI := 1 upto nLen
     cVersion := aReset[nI]
     nMode := RegistryDWord( HKEY_CURRENT_USER, “Software\Adobe\Acrobat Reader\” + cVersion + “\Privileged”, “bProtectedMode” )
     if nMode == 0
      SetRegistryDWord( HKEY_CURRENT_USER, “Software\Adobe\Acrobat Reader\” + cVersion + “\Privileged”, “bProtectedMode”, 1 )
      AAdd( aReset, cVersion )
     endif
    next

    Windows Service and WinINet

    May 20
    2015

    Today unfortunately I have discovered that WinINet calls don’t work in a service.

    I need to put a file with FTP somewhere and call than a URL with http to update the data.

    Microsoft says that instead of WinINet in a service the WinHTTP functions should be used. Unfortunately, WinHTTP has no support for FTP, so you need to use a 3rd party library in a Win32 service. (I used FCE from Marshallsoft).
    And then: the WinHTTP interface is much more complicated to call than WinINET, and after working for a while on the WinHTTP interface (unfortunately it is not implemented in Visual Objects) I decided to use an external call to cURL.

    Detecting paste operation in Richtext control

    Apr 09
    2014

    Today, I had the need to intercept the paste of text in a RTF control to remove some formattings (font type and size).

    Unfortunately the Richtext control is a special beast, and this is not possible before Windows 8.

    On Windows 8, EN_CLIPFORMAT message is sent to the owner window, but unfortunately at this moment there is no way to do this on versions before (I need Windows 7 support). More informations you can find on stackoverflow.com:

    http://stackoverflow.com/questions/5618162/detecting-if-paste-event-occurred-inside-a-rich-text-box

    My (not perfect) solution consists in a extra context menu on the control that calls my own Paste method.

    Open firewall from your application

    Jan 28
    2014

    Since I had added SSL support to the SMTP sending from my application using stunnel, some users don’t opened up the firewall when requested from the operating system (from Windows Vista up).

    Therefore I needed to open up the firewall directly from the application.

    Since I don’t like to have run the application with elevated rights, I decided to make this change from an external app to spawn from my own application.

    It is very simply to add a firewall rule using the netsh command:

    netsh advfirewall firewall add rule name=”stunnel” dir=in action=allow program=”c:\tools\stunnel.exe” enable=yes profile=any

    (where the stunnel.exe path should be adapted to your own settings).

    The problem now was that this command needed to be run with elevated rights, and there is no option to do so.
    After a quick search on the internet, I found elevate.exe – a small .NET executable that does what I need: launch a program with elevated rights.

    So it was easy: launch elevate.exe (downloadable with sources) and pass as command line the call to netsh.exe.

    Data Corruption with DBF files on Windows 2008 server

    Aug 09
    2011

    Recently I had several cases of missing records on a customers network: sometimes added records were missing, indices were corrupted or records incomplete.

    Since this occurred with an application that on all other customers worked very well for years without any problems, I have researched until I found this link:

    http://www.alaska-software.com/fixes/smb2/overview.shtm

    This MSI adds three entries at

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\LanmanWorkstation\Parameters

    In my application I have added the following code:

    This solved the problem.