Accessing Fonts folder from console application with target framework 3.5 - c#

I need to get the path of a specific font in my c:/windows/ folder
The below code works perfectly when the target framework is 4.0
But my application can target only 3.5 and i need to use this in a console application c#
How can i achieve this ? Thanks.
string arialuniTff = path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts), "arial.TTF");
Error Msg in 3.5 : 'System.Environment.SpecialFolder' does not contain a definition

The fonts folder is typically located at %windir%\Fonts, so you should be able to get the location like this:
Path.Combine(
System.Environment.GetEnvironmentVariable("windir"),
"Fonts");
It is a virtual folder, so in theory it could be located somewhere else. In practice, I've never seen that happen or heard of it happening. (Microsoft is confident enough in this location to reference it on their "how to install a font" page). I'm sure that if you're trying to locate a specific file name like that you have good error handling already, though.
Bonus information:
You might know this already, but if you need to know what classes, methods, etc. are available in a specific version of the .net framework, you can find out from MSDN. Go to the documentation page (say this one on Environment.SpecialFolder), and click on the ".NET Framework 4.5" link in the top left corner and choose a different version to see the page you are looking at as it was in that version.

Please refer How to get the path to CSIDL_COMMON_DOCUMENTS in .NET 3.5?
It provides the location for const int CSIDL_COMMON_DOCUMENTS = 0x002e;.
For Fonts folder, use const int CSIDL_FONTS = 0x0014;
It would be:
[DllImport("shell32.dll"), CharSet = CharSet.Auto]
static extern int SHGetFolderPath(IntPtr hwndOwner, int nFolder, IntPtr hToken, uint dwFlags, [Out] StringBuilder pszPath);
const int CSIDL_FONTS = 0x0014;
const int CSIDL_FLAG_CREATE = 0x8000;
StringBuilder sb = new StringBuilder();
int retVal = SHGetFolderPath(IntPtr.Zero,
CSIDL_FONTS | CSIDL_FLAG_CREATE,
IntPtr.Zero,
0,
sb);
Debug.Assert(retVal >= 0); // assert that the function call succeeded
String folderLocation = sb.ToString();

Related

MIDI Output in Unity

I'm trying to build simple software to connect to a MIDI Output device on Windows in Unity and send MIDI data.
As to avoid re-inventing the wheel, I started with the use of the C# Midi Toolkit on CodeProject built with support for .NET 2.0.
The issue I'm having is that it works fine in the Unity editor but then
fails in the standalone Windows build.
Here is the basic connection/play sound code:
// Log devices
int deviceCount = OutputDevice.DeviceCount;
for (int i = 0; i < deviceCount; i++)
{
Debug.Log(string.Format("Detected MIDI Device with ID {0}:{1}", i, OutputDevice.GetDeviceCapabilities(i).name));
}
deviceID = 1;
Debug.Log(string.Format("Connected to {0}", deviceID));
// Connect to device
device = new OutputDevice(deviceID);
// Play Middle C
device.Send(new ChannelMessage(ChannelCommand.NoteOn, 0, note, 127));
And in the standalone build I get the following exception:
OutputDeviceException: The specified device handle is invalid.
I looked through the source and noticed that the library is using
Win32 handles to winmm.dll, I figured this might have something to do with it but not certain where to go from here.
Can anyone provide any insight in how to approach this? I'll probably look at alternatives built specifically for Unity but I'm interested in learning why something like this wouldn't work in the first place.
I don't know if this kind of problem, but the x86 definition of midiOutOpen function that this old codeproject code uses (OutputDevice.cs) is
[DllImport("winmm.dll")]
50 private static extern int midiOutOpen(ref int handle, int deviceID,
51 MidiOutProc proc, int instance, int flags);
While on Pinvoke I can find this definition:
[DllImport("winmm.dll")]
static extern uint midiOutOpen(out IntPtr lphMidiOut, uint uDeviceID, IntPtr dwCallback, IntPtr dwInstance, uint dwFlags);
Maybe it is a platform problem.
You can take a look at how it's implemented in DryWetMIDI, for example: Output device.
Usage:
using Melanchall.DryWetMidi.Devices;
using Melanchall.DryWetMidi.Core;
// ...
using (var outputDevice = OutputDevice.GetByName("Output device")) // or GetById(1)
{
outputDevice.SendEvent(new NoteOnEvent());
}

Why does WNetGetUniversalName fail with ERROR_NOT_SUPPORTED on some machines?

I've reached the end of my rope with this one. I have some C# code that's trying to resolve paths containing drive letters mapped to network drives (e.g. "S:\") to UNC paths (e.g. "\\server\share\"). I'm doing this using WNetGetUniversalName via P/Invoke, but on some of my colleagues' machines (not mine, annoyingly) I'm seeing the function consistently fail with the error code ERROR_NOT_SUPPORTED.
Here's the code:
[DllImport("mpr.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.U4)]
static extern int WNetGetUniversalName(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
/// <summary>
/// Gets the UNC path for the path passed in.
/// </summary>
/// <param name="path">The path for which we want the UNC path.</param>
/// <returns>The UNC path. Returns empty string if an error has occurred. </returns>
public static string GetUniversalPath(string path)
{
const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int ERROR_MORE_DATA = 234;
const int NOERROR = 0;
string retVal = null;
// Pointer to the memory buffer to hold the result.
IntPtr buffer = IntPtr.Zero;
try
{
// First, call WNetGetUniversalName to get the size.
// Passing (IntPtr)IntPtr.Size as the third parameter because WNetGetUniversalName doesn't
// like NULL, and IntPtr.Size will always be a properly-aligned (if not actually valid)
// IntPtr value.
int size = 0;
int apiRetVal = WNetGetUniversalName(path, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size);
if (apiRetVal == ERROR_MORE_DATA)
{
// Allocate the memory.
buffer = Marshal.AllocCoTaskMem(size);
// Now make the call.
apiRetVal = WNetGetUniversalName(path, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size);
if (apiRetVal == NOERROR)
{
// Now get the string. It's all in the same buffer, but
// the pointer is first, so offset the pointer by IntPtr.Size
// and pass to PtrToStringAnsi.
retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size), size);
retVal = retVal.Substring(0, retVal.IndexOf('\0'));
}
}
}
catch
{
// I know swallowing exceptions is nasty...
retVal = "";
}
finally
{
Marshal.FreeCoTaskMem(buffer);
}
return retVal;
}
I'm seeing the ERROR_NOT_SUPPORTED return value the first time WNetGetUniversalName is called. Like I said, it works every time on my machine but it always seems to fail on others.
UPDATE: I should probably add that in all cases, the operating system in use is Windows 7 Enterprise x64 with Service Pack 1.
UPDATE 2: I should have been clearer about the reason for my confusion. The documentation states that ERROR_NOT_SUPPORTED means that no network providers support UNC names. However, UNC names work fine on every machine where I've seen this problem. I'm wondering if anyone has seen this before and/or might be able to provide other possible explanations for ERROR_NOT_SUPPORTED being returned.
The documentation says that this return value means:
The dwInfoLevel parameter is set to UNIVERSAL_NAME_INFO_LEVEL, but the network provider does not support UNC names. (None of the network providers support this function.)
There's not much more to add to that.
Got it! After a bit of digging, I found the source of the problem. The machines where the call didn't work had a network provider called "Pismo File Mount" installed, apparently as part of something called the Pismo File Mount Audit Package.
I found the list of installed network providers as follows:
Open Network and Sharing Center.
Click "Change adapter settings" in the pane on the left.
Press ALT to bring up the menu bar, then select "Advanced Settings..." from the "Advanced" menu.
Go to the "Provider Order" tab.
This is what I saw:
I was able to resolve the problem by either:
Moving "Pismo File Mount" down below "Microsoft Windows Network" in the list of providers.
Uninstalling the Pismo File Mount Audit Package.
I still don't understand the problem as well as I'd like, but at least we have something to tell customers if they run into it.

"No Disk" error using GDAL from C#/.NET

I am using Tamas Szekeres builds of GDAL including the C# bindings in a desktop GIS application using C# and .net 4.0
I am including the entire GDAL distribution in a sub-directory of my executable with the following folder structure:
\Plugins\GDAL
\Plugins\GDAL\gdal
\Plugins\GDAL\gdal-data
\Plugins\GDAL\proj
We are using EPSG:4326, and the software is built using 32-bit target since the GDAL C# API is using p/invoke to the 32-bit libraries (could try 64 bit since Tamas provides these, haven't gotten around to it yet).
When I run my application I get the following error
This error typically happens when software tries to access a device that is no longer attached, such as a removable drive. It is not possible to "catch" this exception because it pops up a system dialog.
After dismissing the dialog using any of the buttons, the software continues to execute as designed.
The error occurs the first time I call the following method
OSGeo.OSR.CoordinateTransformation.TransformPoint(double[] inout);
The strange stuff:
The error occurs on one, and only one computer (so far)
I've run this software in several other computers both 32 and 64 bit without problems
The error does not ocurr on the first run after compiling the GDAL shim library I am using, it only occurrs on each subsequent run
it happens regardless of release, or debug builds
it happens regardless of whether the debugger is attached or not
it happens regardless of whether I turn on or off Gdal.UseExceptions or Osr.UseExceptions();
disabling removable drives causes the bug to disappear. This is not what I consider a real solution as I will not be able to ask a customer to do this.
I have tried the following:
catching the error
changing GDAL directories and environment settings
changing computers and operating systems: this worked
used SysInternals ProcMon to trace what files are being opened with no luck, they all appear to be files that exist
I re-built the computer in question when the hard drive failed, to no avail.
"cleaning" the registry using CCleaner
files in GDAL Directory are unchanged on execution
Assumptions
Error is happening in unmanaged code
During GDAL initialization, some path is referring to a drive on the computer that is no longer attached.
I am also working on the assumption this is limited to a computer configuration error
Configuration
Windows 7 Pro
Intel Core i7 920 # 2,67GHz
12.0 GB RAM
64-bit OS
Drive C: 120 GB SSD with OS, development (Visual Studio 10), etc
Drive D: 1 TB WD 10,000k with data, not being accessed for data.
The Question
I either need a direction to trap the error, or a tool or technique that will allow me to figure out what is causing it. I don't want to release the software with the possibility that some systems will have this behaviour.
I have no experience with this library, but perhaps some fresh eyes might give you a brainwave...
Firstly, WELL WRITTEN QUESTION! Obviously this problem really has you stumped...
Your note about the error not occurring after a rebuild screams out: Does this library generate some kind of state file, in its binary directory, after it runs?
If so, it is possible that it is saving incorrect path information into that 'configuration' file, in a misguided attempt to accelerate its next start-up.
Perhaps scan this directory for changes between a 'fresh build' and 'first run'?
At very least you might find a file you can clean up on shut-down to avoid this alert...
HTH
Maybe you can try this:
Run diskmgmt.msc
Change the driveletter for Disk 2 (right click) if my assumption that Disk 2 is a Removable Disk is true
Run your application
If this removes the error, something in the application is referring to the old driveletter
It could be in the p/invoked libs
Maybe see: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46501 It talks about gcc somehow compiling a driveletter into a binary
+1 Great question, but It is not possible to "catch"
Its one of these awful solutions that will turn up on DailyWTF in 5 years. But for now it is stored here http://www.pinvoke.net/default.aspx/user32.senddlgitemmessage
using Microsoft.VisualBasic; //this reference is for the Constants.vbNo;
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern IntPtr SendDlgItemMessage(IntPtr hDlg, int nIDDlgItem, uint Msg, UIntPtr wParam, IntPtr lParam);
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
// For Windows Mobile, replace user32.dll with coredll.dll
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
// Find window by Caption only. Note you must pass IntPtr.Zero as the first parameter.
[DllImport("user32.dll", EntryPoint = "FindWindow", SetLastError = true)]
static extern IntPtr FindWindowByCaption(IntPtr ZeroOnly, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetDlgItemText(IntPtr hDlg, int nIDDlgItem,[Out] StringBuilder lpString, int nMaxCount);
public void ClickSaveBoxNoButton()
{
//In this example, we've opened a Notepad instance, entered some text, and clicked the 'X' to close Notepad.
//Of course we received the 'Do you want to save...' message, and we left it sitting there. Now on to the code...
//
//Note: this example also uses API calls to FindWindow, GetDlgItemText, and SetActiveWindow.
// You'll have to find those separately.
//Find the dialog box (no need to find a "parent" first)
//classname is #32770 (dialog box), dialog box title is Notepad
IntPtr theDialogBoxHandle; // = null;
string theDialogBoxClassName = "#32770";
string theDialogBoxTitle = "Notepad";
int theDialogItemId = Convert.ToInt32("0xFFFF", 16);
StringBuilder theDialogTextHolder = new StringBuilder(1000);
//hardcoding capacity - represents maximum text length
string theDialogText = string.Empty;
string textToLookFor = "Do you want to save changes to Untitled?";
bool isChangeMessage = false;
IntPtr theNoButtonHandle; // = null;
int theNoButtonItemId = (int)Constants.vbNo;
//actual Item ID = 7
uint theClickMessage = Convert.ToUInt32("0x00F5", 16);
//= BM_CLICK value
uint wParam = 0;
uint lParam = 0;
//Get a dialog box described by the specified info
theDialogBoxHandle = FindWindow(theDialogBoxClassName, theDialogBoxTitle);
//a matching dialog box was found, so continue
if (theDialogBoxHandle != IntPtr.Zero)
{
//then get the text
GetDlgItemText(theDialogBoxHandle, theDialogItemId, theDialogTextHolder, theDialogTextHolder.Capacity);
theDialogText = theDialogTextHolder.ToString();
}
//Make sure it's the right dialog box, based on the text we got.
isChangeMessage = Regex.IsMatch(theDialogText, textToLookFor);
if ((isChangeMessage))
{
//Set the dialog box as the active window
SetActiveWindow(theDialogBoxHandle);
//And, click the No button
SendDlgItemMessage(theDialogBoxHandle, theNoButtonItemId, theClickMessage, (System.UIntPtr)wParam, (System.IntPtr)lParam);
}
}
It turns out there was no way to definitely answer this question.
I ended up "solving" the problem by figuring out that there was some hardware registered on the system that wasn't present. It is still a mystery to me why, after several years, only GDAL managed to provoke this bug.
I will put the inability to catch this exception down to the idiosyncrasies involved with p/invoke and the hardware error thrown at a very low level on the system.
You could add custom error handlers to gdal. This may help:
Link
http://trac.osgeo.org/gdal/ticket/2895

Why does FindMimeFromData recognize image/tiff on one host, but doesn't on another one?

I'm using FindMimeFromData from urlmon.dll for sniffing uploaded files' MIME type. According to MIME Type Detection in Internet Explorer, image/tiff is one of the recognized MIME types. It works fine on my development machine (Windows 7 64bit, IE9), but doesn't work on the test env (Windows Server 2003 R2 64bit, IE8) - it returns application/octet-stream instead of image/tiff.
The above article describes the exact steps taken to determine the MIME type, but since image/tiff is one of the 26 recognized types, it should end on step 2 (sniffing the actual data), so that file extensions and registered applications (and other registry stuff) shouldn't matter.
Oh and by the way, TIFF files actually are associated with a program (Windows Picture and Fax Viewer) on the test server. It's not that any reference to TIFF is absent in Windows registry.
Any ideas why it doesn't work as expected?
EDIT: FindMimeFromData is used like this:
public class MimeUtil
{
[DllImport("urlmon.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = false)]
private static extern int FindMimeFromData(
IntPtr pBC,
[MarshalAs(UnmanagedType.LPWStr)] string pwzUrl,
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1, SizeParamIndex = 3)] byte[] pBuffer,
int cbSize,
[MarshalAs(UnmanagedType.LPWStr)] string pwzMimeProposed,
int dwMimeFlags,
out IntPtr ppwzMimeOut,
int dwReserved);
public static string GetMimeFromData(byte[] data)
{
IntPtr mimetype = IntPtr.Zero;
try
{
const int flags = 0x20; // FMFD_RETURNUPDATEDIMGMIMES
int res = FindMimeFromData(IntPtr.Zero, null, data, data.Length, null, flags, out mimetype, 0);
switch (res)
{
case 0:
string mime = Marshal.PtrToStringUni(mimetype);
return mime;
// snip - error handling
// ...
default:
throw new Exception("Unexpected HRESULT " + res + " returned by FindMimeFromData (in urlmon.dll)");
}
}
finally
{
if (mimetype != IntPtr.Zero)
Marshal.FreeCoTaskMem(mimetype);
}
}
}
which is then called like this:
protected void uploader_FileUploaded(object sender, FileUploadedEventArgs e)
{
int bsize = Math.Min(e.File.ContentLength, 256);
byte[] buffer = new byte[bsize];
int nbytes = e.File.InputStream.Read(buffer, 0, bsize);
if (nbytes > 0)
string mime = MimeUtil.GetMimeFromData(buffer);
// ...
}
I was unable to reproduce your problem, however I did some research on the subject. I believe that it is as you suspect, the problem is with step 2 of MIME Type Detection: the hard-coded tests in urlmon.dll v9 differ from those in urlmon.dll v8.
The Wikipedia article on TIFF shows how complex the format is and that is has been a problem from the very beginning:
When TIFF was introduced, its extensibility provoked compatibility problems. The flexibility in encoding gave rise to the joke that TIFF stands for Thousands of Incompatible File Formats.
The TIFF Compression Tag section clearly shows many rare compression schemes that, as I suspect, have been omitted while creating the urlmon.dll hard-coded tests in earlier versions of IE.
So, what can be done to solve this problem? I can think of three solutions, however each of them brings different kind of new problems along:
Update the IE on your dev machine to version 9.
Apply the latest IE 8 updates on your dev machine. It is well known that modified versions of urlmon.dll are introduced frequently (eg. KB974455). One of them may contain the updated MIME hard-coded tests.
Distribute own copy of urlmon.dll with your application.
It seems that solutions 1 and 2 are the ones you should choose from. There may be a problem, however, with the production environment. As my experience shows the administrators of production env often disagree to install some updates for many reasons. It may be harder to convince an admin to update the IE to v9 and easier to install an IE8 KB update (as they are supposed to, but we all know how it is). If you're in control of the production env, I think you should go with solution 1.
The 3rd solution introduces two problems:
legal: It may be against the Microsoft's policies to distribute own copy of urlmon.dll
coding: you have to load the dll dynamically to call the FindMimeFromData function or at least customize your app's manifest file because of the Dynamic-Link Library Search Order. I assume you are aware, that it is a very bad idea just to manually copy a newer version of urlmon.dll to the system folder as other apps would most likely crash using it.
Anyway, good luck with solving your urlmon riddle.

Cannot Load C++ DLL in C#

>My previous thread<
I created this one,because I installed WinXP on VMBox and I cannot get it working again.
This time I created an OnLoad Event on my form
if (LoadLibrary("blowfish.dll") == 0)
{
Misc.LogToFile("Could not load dll", true);
Application.Exit();
}
Runs fine on my PC,but on VMBox LoadLibrary returns 0.
Some users mentioned that the problem would be in mixing older NET Framework(2.0) with dlls made on newest MS Visual studio(2008 SP1) so I took action and now the program properties it's set to work with NET 3.5
On the VMBox I have NET 2.0,but this is not the problem - the program itself runs fine.I also have C++ Redistributable(2005,2005 SP1 and 2008).
What could be the problem?
To further trouble should you could call
Marshal.GetLastWin32Error();
which should give you an error code.
Is it possible that you deployed a debug version of your native dll which also requires a debug version of MSVCR90D.DLL? You should have distributed the release version because the debug version requires a different set of dlls to be present on the target system.
It obviously works on your development machine because all debug versions of the required libraries come with Visual Studio.
This is how you would get the message belonging to an error code:
[DllImport("kernel32.dll")]
private static extern int FormatMessage(int dwFlags,
IntPtr lpSource, int dwMessageId, int dwLanguageId,
out string lpBuffer, int nSize, IntPtr pArguments);
public static string GetErrorMessage(int errorCode)
{
const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
const int FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
string lpMsgBuf;
int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
int retVal = FormatMessage(dwFlags, IntPtr.Zero, errorCode, 0,
out lpMsgBuf, 0, IntPtr.Zero);
if (0 == retVal)
{
return null;
}
return lpMsgBuf;
}
Call GetLastError after LoadLibrary, check the error code value here: http://msdn.microsoft.com/en-us/library/ms681381.aspx
and see if that helps.
It could be that the dll's location is on the path in one environment and not in the other. It could also be permissions in one environment are not the same as the other.
Try running dependency walker on the DLL - see if any modules are missing.

Categories

Resources