CommandTree (runterladen und “.cmdtree <pfad>”)
SAPPing – simpler SAP .NET Connector Client mit SNC Logon
Weil ich das für einen SAP Call mit DirectAccess (ja, der SAP .NET Connector verhält sich anders als das was in SAP GUI/NWBC eingebaut ist in diesem Umfeld) die Verbindung nachprogrammiert habe und vielleicht jemand das auch brauchen könnte – im Projekt ist nur SAPNCO.DLL und SAPNCO_UTILS.DLL von SAP zu referenzieren.
Dreist von codeproject geklaut und angepasst (SNC)/vereinfacht – außerdem kann man hier den Text kopieren und hat nicht nur Screenshots 😀
using System;
using SAP.Middleware.Connector;
namespace SAPPing
{
public class SAPDestinationConfig:IDestinationConfiguration
{
public bool ChangeEventsSupported()
{
return false;
}
public event RfcDestinationManager.ConfigurationChangeHandler ConfigurationChanged;
public RfcConfigParameters GetParameters(string destinationName)
{
RfcConfigParameters par=new RfcConfigParameters();
par.Add(RfcConfigParameters.Name, "ABC");
// Direktverbindung (AppServerHost) oder via MessageServer (=LoadBalanced)
par.Add(RfcConfigParameters.AppServerHost, "abcsaphost.mydomain.com");
//par.Add(RfcConfigParameters.MessageServerHost, "abcsaphost.mydomain.com");
//par.Add(RfcConfigParameters.MessageServerService, "1234"); // port vonLandscape XML
par.Add(RfcConfigParameters.SystemNumber, "11");
par.Add(RfcConfigParameters.SystemID, "ABC");
par.Add(RfcConfigParameters.Client, "999"); // "Mandant"
par.Add(RfcConfigParameters.Language, "DE");
par.Add(RfcConfigParameters.PoolSize, "10");
par.Add(RfcConfigParameters.SncPartnerName, "p:CN=whatever/abc@mydomain.com"); // von Landscape XML
par.Add(RfcConfigParameters.SncMyName, "...Rechtsklick in SecureLogin und 'Copy SNC name to clipboard' - auch irgendwas mit 'p:CN=user@mydomain.com' bei Kerberos");
par.Add(RfcConfigParameters.SncMode, "1"); // SNC benutzen
par.Add(RfcConfigParameters.SncQOP, "9"); // "beste" SNC Methode automatisch wählen
par.Add(RfcConfigParameters.Trace, "4"); // volles Tracing
return par;
}
}
class Program
{
static void Main(string[] args)
{
RfcTrace.TraceDirectory = @"C:\TEMP\SAP";
RfcTrace.DefaultTraceLevel = (uint)RfcTracing.Level4;
GeneralConfiguration.CPICTraceLevel = 3;
IDestinationConfiguration config = new SAPDestinationConfig();
config.GetParameters("ABC");
if (RfcDestinationManager.TryGetDestination("ABC") == null)
{
RfcDestinationManager.RegisterDestinationConfiguration(config);
}
try
{
RfcDestination dest = RfcDestinationManager.GetDestination("ABC");
dest.Ping();
Console.WriteLine($"SAP ping OK, connected to {dest.SystemAttributes.PartnerHost} running {dest.SystemAttributes.PartnerRelease}");
}
catch (Exception oExc)
{
Console.WriteLine(oExc.ToString());
}
}
}
}
Kiosk App mit WebView2 in F#
Auch mit Visual Studio 2022 ist F# noch immer ein Stiefkind was WinForms Entwicklung angeht, trotzdem sind Dinge wie gehabt wesentlich knapper, präziser und je nach Geschmack schöner mit F# abzubilden – im speziellen eine sehr einfache Kiosk App die nichts anderes als eine Seite Anzeigen soll von der man am besten nicht “entkommt”:
- Kein Kontextmenü
- Keine Downloads bzw. nur für Autostart (bestimmte Dateiextension)
- Keine neuen Fenster
- Nur bestimmte URLs (Regex) erlaubt
Ergebnis:
open System
open System.Windows.Forms
open System.IO
open System.Text.RegularExpressions
open System.Diagnostics
open Microsoft.Web.WebView2.WinForms
open Microsoft.Web.WebView2.Core
let InitForm (env:CoreWebView2Environment) =
let webViewForm = new Form(Text = "Kiosk Titel", Width=int (float Screen.PrimaryScreen.Bounds.Width * 0.8), Height=int (float Screen.PrimaryScreen.Bounds.Height * 0.6), StartPosition=FormStartPosition.CenterScreen, ShowIcon=false)
let webView = new WebView2()
webView.Dock <- System.Windows.Forms.DockStyle.Fill
webView.Location <- new System.Drawing.Point(0, 0)
webViewForm.Controls.Add webView
webView.EnsureCoreWebView2Async(env) |> ignore
webView.CoreWebView2InitializationCompleted.AddHandler(fun sender args ->
webView.CoreWebView2.Settings.AreDefaultContextMenusEnabled <- false
webView.CoreWebView2.Settings.AreDevToolsEnabled <- false
webView.CoreWebView2.Settings.AreBrowserAcceleratorKeysEnabled <- false
webView.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled <- false
webView.CoreWebView2.Settings.AreHostObjectsAllowed <- false
webView.CoreWebView2.Settings.IsPasswordAutosaveEnabled <- false
webView.CoreWebView2.Settings.IsGeneralAutofillEnabled <- false
webView.CoreWebView2.Settings.IsWebMessageEnabled <- false
webView.CoreWebView2.DownloadStarting.AddHandler(fun downloadsender downloadargs ->
let deferral = downloadargs.GetDeferral()
System.Threading.SynchronizationContext.Current.Post(fun _ ->
downloadargs.Handled <- true // no download dialog
downloadargs.DownloadOperation.StateChanged.AddHandler(fun statesender stateargs ->
if (downloadargs.DownloadOperation.State = CoreWebView2DownloadState.Completed) then
if (System.IO.Path.GetExtension(downloadargs.DownloadOperation.ResultFilePath).ToUpper() = ".XXX") then // valid extensions to start
let proc=new ProcessStartInfo(downloadargs.DownloadOperation.ResultFilePath)
proc.UseShellExecute <- true
Process.Start(proc) |> ignore
)
deferral.Dispose()
, null)
)
webView.CoreWebView2.NavigationStarting.AddHandler(fun navigatesender navigateargs ->
if (Regex.Match(navigateargs.Uri,"^https://(mysite|myothersite)\.local/.*", RegexOptions.IgnoreCase).Success = false) then navigateargs.Cancel <- true
)
webView.CoreWebView2.NewWindowRequested.AddHandler(fun newwindowsender newwindowargs ->
newwindowargs.Handled <- true // do not open window
)
webView.CoreWebView2.Navigate "https://mysite.local" |> ignore
)
webViewForm
[<EntryPoint; STAThread>]
let main argv =
let userDataFolder = $"{Path.GetTempPath()}\KioskWebUserData"
let options = new CoreWebView2EnvironmentOptions("--kiosk")
let env=CoreWebView2Environment.CreateAsync("",userDataFolder,options).GetAwaiter().GetResult()
Application.EnableVisualStyles()
Application.SetCompatibleTextRenderingDefault(false)
Application.Run(InitForm env)
0
Projektdatei:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWindowsForms>true</UseWindowsForms>
</PropertyGroup>
<ItemGroup>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1020.30" />
</ItemGroup>
</Project>
Manjaro schwarzer Bildschirm nach Update
Großes Update mit 98 Paketen – inklusive Linux Kernel, nach Reboot direkt nach GRUB (ESC nicht probiert) schwarzer Schirm (aber Signal, Bildschirm schaltet nicht ab).
Grund: Linux Kernel bzw. INITRAMFS offenbar nicht richtig erzeugt.
Lösung:
Mit Live Stick booten
sudo su -
manjaro-chroot -a
pacman -Q|grep linux
pacman -S linuxXXX
CTRL+D um chroot zu beenden
reboot
System.Data.SqlClient Koexistenz .NET 4.x und .NET Core 3+/.NET 5+
Hat man ein .NET Core/.NET 5+ Projekt welches eine .NET 4.x Library verwendet die wiederum System.Data.SqlClient (SqlConnection, SqlTransaction, etc.) verwendet läuft man in diese beiden Fehler (hier von einem F# Web API Projekt):
FS1108 The type 'SqlConnection' is required here and is unavailable. You must add a reference to assembly 'System.Data.SqlClient, Version=0.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.
FS1109 A reference to the type 'System.Data.SqlClient.SqlConnection' in assembly 'System.Data' was found, but the type could not be found in that assembly
Gerne wird im Internetz dann darauf verwiesen dass .NET Core/.NET 5+ System.Data.SqlClient nicht mehr per Default referenziert und man doch das gleichnamige Nuget installieren solle – nur hilft das halt nur wenn man z.B. eine alte .NET Framework Anwendung auf .NET Core/.NET 5+ bringt; beim Referenzierungsfall (.NET neu referenziert .NET alt) bringt das nichts.
Dort ist dann sowohl das alte als auch das neue Projekt auf das neue Nuget Microsoft.Data.SqlClient umzustellen – liefert die gleichen Klassen und funktionieren dann auch in beiden Welten – das “alte” .NET 4.x Projekt muss dafür aber (aktuell) auf Minimum .NET 4.6.1 sein.
Also Nuget installieren und die usings/opens von System.Data.SqlClient auf Microsoft.DataSqlClient umstellen – fertig.
Referenz: https://devblogs.microsoft.com/dotnet/introducing-the-new-microsoftdatasqlclient/
Powershell Prompt mit Timestamp
Über Function “prompt” – in $PSHome\Profile.ps1 (alle User) oder $Home\Documents\WindowsPowershell\Profile.ps1 (aktueller User) gepackt für automatische Aktivierung:
function prompt { return "[$([System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss"))] $(Get-Location)> "}
Wenn man in einem constrained environment arbeitet und/oder Profile unterbunden werden kann man auch powershell direkt damit starten:
PowerShell -noexit -command "function prompt { return '['+[System.DateTime]::Now.ToString('yyyy-MM-dd HH:mm:ss')+']'+ (Get-Location)+'> '}"
Visual Studio: Signatur des Targets
In den Project Properties unter “Build Events” eine Post-build event command line einfügen:
"c:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64\signtool.exe" sign /v /sha1 "<sha1 thumbprint vom zertifikat>" /fd "SHA256" /tr http://rfc3161timestamp.globalsign.com/advanced /td "SHA256" $(TargetPath)
Signtool Pfad ist natürlich abhängig von den lokalen Gegebenheiten, ebenso kann man sich anderen Timestampservice aussuchen.
Igel UMS: TCs ohne fixierten DNS Hostnamen finden
Igel TCs melden sich per Default als ITC<macadresse> im Management wenn man ihnen nicht per DHCP oder eben statisch im Management einen Namen gibt – wenn man zwischen DHCP mit Option 12 (Hostname) und DHCP ohne Option 12 wechselt funktioniert der Wechsel nicht immer. Mit dieser Query findet man alle TCs in einem (oder mehreren) TC-Verzeichnissen:
select tc.tcname,tc.network_name,tc.macaddress,tc.movedtobin from thinclient as tc where tc.tcid in (select td.tcid from thinclientisindirectory as td where dirid in (select d.dirid from directories as d where d.name like '%MyDirectory%') ) and (select count(*) from thinclientsettings as tcs where tcs.tcid=tc.tcid and tcs.classname='network.dns.hostname') = 0
Umgekehrt kann man hiermit die konfigurierten DNS Namen rausfinden:
select tc.tcname,tc.network_name,tc.macaddress,tc.movedtobin from thinclient as tc where tc.tcid in (select td.tcid from thinclientisindirectory as td where dirid in (select d.dirid from directories as d where d.name like '%MyDirectory%') ) and (select count(*) from thinclientsettings as tcs where tcs.tcid=tc.tcid and tcs.classname='network.dns.hostname') = 0
SQL Server Express TCP abdrehen
Rausfinden:
$cn="mysqlexpress";(gwmi -ComputerName $cn -Namespace "root\microsoft\sqlserver\$((gwmi -ComputerName $cn -Namespace "root\microsoft\sqlserver" -Query "select * from __NAMESPACE where Name like 'ComputerManagement%'").Name)" -Query "select * from ServerNetworkProtocol where ProtocolName='Tcp'").Enabled
Abdrehen:
$cn="mysqlexpress"; iwmi -InputObject (gwmi -ComputerName $cn -Namespace "root\microsoft\sqlserver\$((gwmi -ComputerName $cn -Namespace "root\microsoft\sqlserver" -Query "select * from __NAMESPACE where Name like 'ComputerManagement%'").Name)" -Query "select * from ServerNetworkProtocol where ProtocolName='Tcp'") -Name "SetDisable"
(geht davon aus dass nur eine Version vom SQL Server installiert ist – sollten es mehr sein muss man halt ein ForEach-Object einbauen)
Digitale Signatur mit Powershell
Weil nicht alle Timestampdienstleister mit Powershell funktionieren und ich es leid bin dauern einzufahren:
Set-AuthenticodeSignature -Certificate (gci cert:currentuser\my -CodeSigningCert) -TimestampServer http://timestamp.sectigo.com ...myfile...
(geht natürlich davon aus dass im Userzertifikatsstore genau ein Code Signing Zertifikat liegt)