We use external component (MigraDoc) to compose an RTF document. Which then is converted to plain text by assigning RTF as string to System.Windows.Forms.RichTextBox's Rtf field and reading Text field. This has worked earlier but now we have found a problem (which has been there for a while already).
Plain text conversion is not working on Windows 10 but same application is working on Windows 7. After assigning Rft field, the Text field remains empty and also Rft field doesn't have the value which was just assigned. *
However, earlier version of our application is working on Windows 10 as well. Even there are no direct constitutive changes on this area. One possibly affecting change is .Net target version change from 4.0 to 4.7.2 (but it is hard to verify this anymore).
If I take the RTF string from Windows 7 and save it as file, it opens on WordPad on Windows 7. But it doesn't open on WordPad on Windows 10.
Have somebody else phased similar issues? Or are there any ideas how this could be fixed?
* But instead value:
{\rtf1\ansi\ansicpg1252\deff0\nouicompat\deflang1033{\fonttbl{\f0\fnil\fcharset0 Microsoft Sans Serif;}}
{\*\generator Riched20 10.0.19041}\viewkind4\uc1
\pard\f0\fs17\par
}
EDIT:
MigraDoc version is 1.32 i.e. the latest non-beta.
If you want to try out the RICHEDIT20W version of RichEdit Control (Rich Text Edit Control v. 3.1), use a Custom Control built like this one. It tries to load the riched20.dll library and, if it succeeds, it then sets the Class name of the Control in the CreateParams override.
You could also try to load the RICHEDIT60W version that is usually shipped with MS Office installations, for testing. This version has also different behavior.
In this case, you have to provide the full path of the library, which depends on the installed Office version and bitness
In practice, you have means to use a specific version of the Control.
Tweak this code to make it act as you prefer. As it is, it allows to switch between ver. RICHEDIT20W and RICHEDIT50W (design-time or run-time)
using System.ComponentModel;
using System.Runtime.InteropServices;
public class RichTextBox20W : RichTextBox {
private bool m_UseRichedit20 = true;
public RichTextBox20W() {
IsRichEdit20Available = LoadLibrary("riched20.dll") != IntPtr.Zero;
}
[DefaultValue(true)]
public bool UseRichedit20 {
get => m_UseRichedit20 & IsRichEdit20Available;
set {
if (value != m_UseRichedit20) {
m_UseRichedit20 = value;
RecreateHandle();
}
}
}
public bool IsRichEdit20Available { get; }
protected override CreateParams CreateParams {
get {
var cp = base.CreateParams;
if (UseRichedit20) {
cp.ClassName = "RICHEDIT20W";
}
// If the library is not found, the class name is set to RICHEDIT50W
// which is the default when targeting .NET Framework 4.7.2+
return cp;
}
}
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
static extern IntPtr LoadLibrary(string lpLibFileName);
}
Related
I published a C# winforms GUI and everything looks as expected on my machine. I went to install on another machine and all my text gets italicized.
Both machines are running windows 10 and have the same screen resolution settings. I also installed my GUI on a third machine and everything works as expected on it.
Is there some setting in Visual Studio I have to set for the fonts to look the same on all machines? Or is there a specific code I need to add?
Here is a snippet of what the GUI should look like (no italics)
GUI on machine #2 (font gets italicized)
Ok so that you could close the question:
Check the Font Settings and ensure the font you want is installed.
There.
Ok so after a ton of SO and Google based on the comments received earlier, I was able to embed the font into my code. Now, on startup, my app checks if the font is installed on the machine. If font is not installed, my app installs it.
This way eliminates having to manually check if the font is installed on each machine that the app will be run on.
Note: need admin privileges for this to work.
First: Embed the font file as a resource
Double-click Resources.resx, and in the toolbar for the designer click Add Resource/Add Existing File and select your .ttf file
In the solution explorer, right-click your .ttf file and go to Properties. Set the 'Build Action' to "Content" and 'Copy To Output Directory' property set to "Copy Always"
Second: Add this code
using Microsoft.Win32;
using System;
using System.Drawing.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace TestAutomation
{
public partial class SplashScreen : Form
{
[DllImport("gdi32.dll", EntryPoint = "AddFontResource")]
public static extern int AddFontResource(string lpFileName);
[DllImport("gdi32.dll")]
private static extern int CreateScalableFontResource(uint fdwHidden, string
lpszFontRes, string lpszFontFile, string lpszCurrentPath);
// <summary>
// Installs font on the user's system and adds it to the registry so it's available on the next session
// Your font must be embedded as a resource in your project with its 'Build Action' property set to 'Content'
// and its 'Copy To Output Directory' property set to 'Copy Always'
// </summary>
private void RegisterFont(string contentFontName)
{
DirectoryInfo dirWindowsFolder = Directory.GetParent(Environment.GetFolderPath(Environment.SpecialFolder.System));
// Concatenate Fonts folder onto Windows folder.
string strFontsFolder = Path.Combine(dirWindowsFolder.FullName, "Fonts");
// Creates the full path where your font will be installed
var fontDestination = Path.Combine(strFontsFolder, contentFontName);
// Check if file exists in destination folder. If not, then copy the file from project directory to destination
if (!File.Exists(fontDestination))
{
try
{
// Copies font to destination
File.Copy(Path.Combine(Directory.GetCurrentDirectory(), contentFontName), fontDestination);
// Retrieves font name
PrivateFontCollection fontCol = new PrivateFontCollection();
fontCol.AddFontFile(fontDestination);
var actualFontName = fontCol.Families[0].Name;
// Add font
AddFontResource(fontDestination);
// Add registry entry
Registry.SetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts",
actualFontName, contentFontName, RegistryValueKind.String);
}
catch (Exception e)
{
MessageBox.Show(e.Message + "\n\nThe required font(s) have not been installed."
+ "\n\nPlease contact your systems administrator for help.");
Start();
}
}
// If file exists in destination folder, then start program.
else
{
Start();
}
}
public SplashScreen()
{
RegisterFont("GOTHIC.TTF");
}
private void Start()
{
InitializeComponent();
}
}
}
I modified the code to fit my needs. Here are the links to where I found my info:
How to quickly and easily embed fonts in winforms app in C#
https://csharp.hotexamples.com/examples/System.Drawing.Text/PrivateFontCollection/AddFontFile/php-privatefontcollection-addfontfile-method-examples.html
I’m at the moment automating the test for a legacy application developed in vb6, which uses a GridEx2000b Control from Janus Systems.
For doing this I’m using Ranorex as my favorite tool for developing the test automation, so that I can develop the test code using c#.
My problem now is to automate the GridEx 2000b control, which Ranorex out of-the-box don’t have any support for. Therefore I’m trying to figure out a solution where I can reference the GrixEx control using the Win32 handle I can find for the control, so I can use the ComInterface from the component to navigate the automate the control.
I have an idea of a solution but I cannot figure out how to do it, where I hope that you guys would be able help me.
The pseudo code for the problem:
using GridEX20;
class GridExWrapper
{
public GridEX20.GridEXClass Instance;
public GridExWrapper(IntPtr win32handle)
{
Instance = (GridEX20.GridEXClass)Win32ControlUtilities.GetControlReference(win32Handle);
}
}
class Win32ControlUtilities
{
public static SomeKindOfHandle GetControlReference(IntPtr win32Handle)
{
...
...
...
}
}
I’ll get the win32handle from Ranorex or some other spy tool.
Then I can use the GridExWrapper like this.
using NUnit.Framework;
class Program
{
[Test]
public void control_should_have_9_items()
{
/// Get win32 handle from Ranorex
IntPtr win32handle = XXXXXX;
int expectedItemCount = 9;
GridEXClass control = new GridExWrapper(win32handle);
Assert.AreEqual(expectedItemCount, control.ItemCount);
}
}
You could try the Microsoft UI Automation library (System.Windows.Automation) for identifying the properties of the control. Sometimes even if Ranorex fails, MSUIA manages to recognize the control as it looks into native properties of a control for identification. Not guaranteed but worth a try.Here is a link to a tutorial on using MSUIA.
I'm trying to set up a way to manage file associations for my program in C#. I already set the correct values in the registry with WiX, and found a wrapper for ApplicationAssociationRegistrationUI which should allow me to open the GUI to set file associations. But it doesn't work. I get the following exception: Element not found. (Exception from HRESULT: 0x80070490)
The wrapper:
namespace FileAssociation
{
[ClassInterface(ClassInterfaceType.None)]
[ComImport]
[Guid("1968106d-f3b5-44cf-890e-116fcb9ecef1")]
[TypeLibType(TypeLibTypeFlags.FCanCreate)]
public sealed class ApplicationAssociationRegistrationUI : IApplicationAssociationRegistrationUI
{
[MethodImpl(MethodImplOptions.InternalCall)]
public extern void LaunchAdvancedAssociationUI(string appRegistryName);
}
[CoClass(typeof(ApplicationAssociationRegistrationUI))]
[ComImport]
[Guid("1f76a169-f994-40ac-8fc8-0959e8874710")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[TypeLibImportClass(typeof(ApplicationAssociationRegistrationUI))]
public interface IApplicationAssociationRegistrationUI
{
void LaunchAdvancedAssociationUI([MarshalAs(UnmanagedType.LPWStr)] string appRegistryName);
}
}
Usage:
var assocUi = new ApplicationAssociationRegistrationUI();
try
{
assocUi.LaunchAdvancedAssociationUI(InstanceManager.ProgId);
}
catch
{
MessageBox.Show("Could not display the file association manager. Please repair the installation and try again.", "Error", MessageBoxButton.OK, MessageBoxImage.Warning);
}
finally
{
Marshal.ReleaseComObject(assocUi);
}
Again, all the correct keys exist in the registry. This is not the first time COM Interop fails miserably for me so I am beginning to think that I must be missing something important. I tried checking "Register for COM Interop" in the project properties, and I tried making it COM-Visible.
I am aware that this only works on Vista or newer, which is fine since my program doesn't support XP anyway. I'm testing it on Windows 8.1, both as Admin and as normal user.
EDIT: It works on Windows 7! On MSDN it does not say that this API was deprecated in Win8...
What have I done wrong? Is there an easier way to do this which I don't know about?
Finally found the problem!!!
Starting from Windows 8, the program needs to have company information (I think this is a bug, since it isn't mentioned on Microsoft's site.)
So make sure to fill out this attribute in AssemblyInfo.cs:
[assembly: AssemblyCompany("YourCompany")]
If it's an empty string it won't work!
Deploying a C# .NET application, our GUI elements use the "Arial" font (that's the default in the Visual Studio GUI designer thing).
One particular customer we're working with for some reason didn't have the Arial font installed (they must have manually deleted it, since as far as I know it comes by default with all Windows installs).
This results in an exception/application crash.
Is there some way to make sure a font exists with C#, and/or install it automatically if it doesn't?
You need to embed the font as a resource and then do something similar to this:
[DllImport("gdi32", EntryPoint = "AddFontResource")]
public static extern int AddFontResourceA(string lpFileName);
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
List<FontFamily> fontsFamilies = new List<FontFamily>(FontFamily.Families);
if (!fontsFamilies.Exists(f => f.Name.Equals("Arial")))
{
//Save the font from resource here....
//Install the font
int result = AddFontResourceA(#"C:\MY_FONT_LOCATION\Arial.TTF");
}
Application.Run(new Form1());
}
Embed the font as a resource and check for/install it before you display any UI elements.
I'm trying to make my application force a theme - this is straightforward as shown here: http://arbel.net/blog/archive/2006/11/03/Forcing-WPF-to-use-a-specific-Windows-theme.aspx
However, I don't know what theme I'm using now. I'm using the Windows XP default theme, whatever that may be. That article says
It's important to specify the version
and the public key token
...where do I get that information?
To get the theme name you can call the unmanaged GetCurrentThemeName method:
public string GetThemeName()
{
StringBuilder themeNameBuffer = new StringBuilder(260);
var error = GetCurrentThemeName(themeNameBuffer, themeNameBuffer.Capacity, null, 0, null, 0);
if(error!=0) Marshal.ThrowExceptionForHR(error);
return themeNameBuffer.ToString();
}
[DllImport("uxtheme.dll", CharSet=CharSet.Auto)]
public static extern int GetCurrentThemeName(StringBuilder pszThemeFileName, int dwMaxNameChars, StringBuilder pszColorBuff, int dwMaxColorChars, StringBuilder pszSizeBuff, int cchMaxSizeChars);
You can find the version and public key token by right-clicking the theme .dll (such as PresentationFramework.Aero) in the GAC (open c:\Windows\Assembly in Exporer), or you can use code to do it. Just loop through all loaded assemblies using AppDomain.CurrentDomain.LoadedAssemblies and find the one you want:
foreach(Assembly a in AppDomain.CurrentDomain.LoadedAssemblies)
if(a.Name.StartsWith("PresentationFramework."))
return a.FullName;
Note that looping through the loaded assemblies will also tell you the current theme name if only one theme has been loaded in the current AppDomain.