├── .gitignore ├── NativeFileDialogSharp ├── nfd.dll ├── nfd_x86.dll ├── runtimes │ ├── win-x64 │ │ └── native │ │ │ └── nfd.dll │ ├── win-x86 │ │ └── native │ │ │ └── nfd.dll │ ├── linux-x64 │ │ └── native │ │ │ └── libnfd.so │ └── osx-x64 │ │ └── native │ │ └── libnfd.dylib ├── NativeFileDialogSharp.csproj ├── Native │ └── NativeFunctions.cs └── NativeWrappers.cs ├── .gitmodules ├── NativeFileDialogSharpSandboxNetFramework ├── nfd.dll ├── nfd_x86.dll ├── packages.config ├── App.config ├── Program.cs ├── Properties │ └── AssemblyInfo.cs └── NativeFileDialogSharpSandboxNetFramework.csproj ├── README.md ├── NativeFileDialogSharpSandbox ├── NativeFileDialogSharpSandbox.csproj └── Program.cs ├── LICENSE └── NativeFileDialogSharp.sln /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | obj/ 3 | /packages/ 4 | riderModule.iml 5 | /_ReSharper.Caches/ -------------------------------------------------------------------------------- /NativeFileDialogSharp/nfd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/nfd.dll -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "nativefiledialog"] 2 | path = nativefiledialog 3 | url = https://github.com/milleniumbug/nativefiledialog 4 | -------------------------------------------------------------------------------- /NativeFileDialogSharp/nfd_x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/nfd_x86.dll -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/nfd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharpSandboxNetFramework/nfd.dll -------------------------------------------------------------------------------- /NativeFileDialogSharp/runtimes/win-x64/native/nfd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/runtimes/win-x64/native/nfd.dll -------------------------------------------------------------------------------- /NativeFileDialogSharp/runtimes/win-x86/native/nfd.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/runtimes/win-x86/native/nfd.dll -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/nfd_x86.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharpSandboxNetFramework/nfd_x86.dll -------------------------------------------------------------------------------- /NativeFileDialogSharp/runtimes/linux-x64/native/libnfd.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/runtimes/linux-x64/native/libnfd.so -------------------------------------------------------------------------------- /NativeFileDialogSharp/runtimes/osx-x64/native/libnfd.dylib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/milleniumbug/NativeFileDialogSharp/HEAD/NativeFileDialogSharp/runtimes/osx-x64/native/libnfd.dylib -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NativeFileDialogSharp 2 | 3 | C# library bindings for [nativefiledialog](https://github.com/mlabbe/nativefiledialog). 4 | 5 | The library currently implements 6 | 7 | - Windows x86 8 | - Windows x86-64 9 | - Linux x86-64 10 | - macOS x86-64 11 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandbox/NativeFileDialogSharpSandbox.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net6.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is provided 'as-is', without any express or implied 2 | warranty. In no event will the authors be held liable for any damages 3 | arising from the use of this software. 4 | 5 | Permission is granted to anyone to use this software for any purpose, 6 | including commercial applications, and to alter it and redistribute it 7 | freely, subject to the following restrictions: 8 | 9 | 1. The origin of this software must not be misrepresented; you must not 10 | claim that you wrote the original software. If you use this software 11 | in a product, an acknowledgment in the product documentation would be 12 | appreciated but is not required. 13 | 2. Altered source versions must be plainly marked as such, and must not be 14 | misrepresented as being the original software. 15 | 3. This notice may not be removed or altered from any source distribution. 16 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandbox/Program.cs: -------------------------------------------------------------------------------- 1 | using NativeFileDialogSharp; 2 | using System; 3 | 4 | namespace NativeFileDialogSharpSandbox 5 | { 6 | internal class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | PrintResult(Dialog.FileOpenMultiple("pdf", null)); 11 | PrintResult(Dialog.FileOpen(null)); 12 | PrintResult(Dialog.FileSave(null)); 13 | PrintResult(Dialog.FolderPicker(null)); 14 | } 15 | 16 | static void PrintResult(DialogResult result) 17 | { 18 | Console.WriteLine($"Path: {result.Path}, IsError {result.IsError}, IsOk {result.IsOk}, IsCancelled {result.IsCancelled}, ErrorMessage {result.ErrorMessage}"); 19 | if (result.Paths != null) 20 | { 21 | Console.WriteLine("Paths"); 22 | Console.WriteLine(string.Join("\n", result.Paths)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/Program.cs: -------------------------------------------------------------------------------- 1 | using NativeFileDialogSharp; 2 | using System; 3 | 4 | namespace NativeFileDialogSharpSandboxNetFramework 5 | { 6 | internal class Program 7 | { 8 | static void Main(string[] args) 9 | { 10 | PrintResult(Dialog.FileOpenMultiple("pdf", null)); 11 | PrintResult(Dialog.FileOpen(null)); 12 | PrintResult(Dialog.FileSave(null)); 13 | PrintResult(Dialog.FolderPicker(null)); 14 | } 15 | 16 | static void PrintResult(DialogResult result) 17 | { 18 | Console.WriteLine($"Path: {result.Path}, IsError {result.IsError}, IsOk {result.IsOk}, IsCancelled {result.IsCancelled}, ErrorMessage {result.ErrorMessage}"); 19 | if (result.Paths != null) 20 | { 21 | Console.WriteLine("Paths"); 22 | Console.WriteLine(string.Join("\n", result.Paths)); 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /NativeFileDialogSharp/NativeFileDialogSharp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7.3 5 | 0.5.0 6 | disable 7 | true 8 | Zlib 9 | milleniumbug 10 | https://github.com/milleniumbug/NativeFileDialogSharp 11 | https://github.com/milleniumbug/NativeFileDialogSharp 12 | Cross-platform native file dialog controls for Windows, Linux and macOS 13 | netstandard2.0 14 | 15 | 16 | 17 | 18 | 19 | PreserveNewest 20 | 21 | 22 | PreserveNewest 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("NativeFileDialogSharpSandboxNetFramework")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("NativeFileDialogSharpSandboxNetFramework")] 13 | [assembly: AssemblyCopyright("Copyright © 2021")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0fd91168-d8f5-4776-8d91-cb9b9c55aa1b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /NativeFileDialogSharp.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31919.166 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeFileDialogSharp", "NativeFileDialogSharp\NativeFileDialogSharp.csproj", "{4127F279-9FD5-4C37-B904-242C124C1A07}" 7 | EndProject 8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NativeFileDialogSharpSandbox", "NativeFileDialogSharpSandbox\NativeFileDialogSharpSandbox.csproj", "{427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NativeFileDialogSharpSandboxNetFramework", "NativeFileDialogSharpSandboxNetFramework\NativeFileDialogSharpSandboxNetFramework.csproj", "{0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Debug|x64 = Debug|x64 16 | Debug|x86 = Debug|x86 17 | Release|Any CPU = Release|Any CPU 18 | Release|x64 = Release|x64 19 | Release|x86 = Release|x86 20 | EndGlobalSection 21 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 22 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|x64.ActiveCfg = Debug|Any CPU 25 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|x64.Build.0 = Debug|Any CPU 26 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|x86.ActiveCfg = Debug|Any CPU 27 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Debug|x86.Build.0 = Debug|Any CPU 28 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|x64.ActiveCfg = Release|Any CPU 31 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|x64.Build.0 = Release|Any CPU 32 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|x86.ActiveCfg = Release|Any CPU 33 | {4127F279-9FD5-4C37-B904-242C124C1A07}.Release|x86.Build.0 = Release|Any CPU 34 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|x64.ActiveCfg = Debug|Any CPU 37 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|x64.Build.0 = Debug|Any CPU 38 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|x86.ActiveCfg = Debug|Any CPU 39 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Debug|x86.Build.0 = Debug|Any CPU 40 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|x64.ActiveCfg = Release|Any CPU 43 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|x64.Build.0 = Release|Any CPU 44 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|x86.ActiveCfg = Release|Any CPU 45 | {427E5F76-8418-4EA3-9AA3-C1DBFDE0478F}.Release|x86.Build.0 = Release|Any CPU 46 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|x64.ActiveCfg = Debug|x64 49 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|x64.Build.0 = Debug|x64 50 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|x86.ActiveCfg = Debug|x86 51 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Debug|x86.Build.0 = Debug|x86 52 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|x64.ActiveCfg = Release|x64 55 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|x64.Build.0 = Release|x64 56 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|x86.ActiveCfg = Release|x86 57 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B}.Release|x86.Build.0 = Release|x86 58 | EndGlobalSection 59 | GlobalSection(SolutionProperties) = preSolution 60 | HideSolutionNode = FALSE 61 | EndGlobalSection 62 | GlobalSection(ExtensibilityGlobals) = postSolution 63 | SolutionGuid = {124CB415-A372-4268-9C13-330A0F1093C2} 64 | EndGlobalSection 65 | EndGlobal 66 | -------------------------------------------------------------------------------- /NativeFileDialogSharpSandboxNetFramework/NativeFileDialogSharpSandboxNetFramework.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {0FD91168-D8F5-4776-8D91-CB9B9C55AA1B} 8 | Exe 9 | NativeFileDialogSharpSandboxNetFramework 10 | NativeFileDialogSharpSandboxNetFramework 11 | v4.6.2 12 | 512 13 | true 14 | true 15 | 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 36 | 37 | true 38 | bin\x64\Debug\ 39 | DEBUG;TRACE 40 | full 41 | x64 42 | 7.3 43 | prompt 44 | true 45 | 46 | 47 | bin\x64\Release\ 48 | TRACE 49 | true 50 | pdbonly 51 | x64 52 | 7.3 53 | prompt 54 | true 55 | 56 | 57 | true 58 | bin\x86\Debug\ 59 | DEBUG;TRACE 60 | full 61 | x86 62 | 7.3 63 | prompt 64 | true 65 | 66 | 67 | bin\x86\Release\ 68 | TRACE 69 | true 70 | pdbonly 71 | x86 72 | 7.3 73 | prompt 74 | true 75 | 76 | 77 | 78 | ..\packages\NativeFileDialogSharp.0.5.0\lib\netstandard2.0\NativeFileDialogSharp.dll 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /NativeFileDialogSharp/Native/NativeFunctions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace NativeFileDialogSharp.Native 5 | { 6 | public struct nfdpathset_t 7 | { 8 | public IntPtr buf; 9 | public IntPtr indices; 10 | public UIntPtr count; 11 | } 12 | 13 | public enum nfdresult_t 14 | { 15 | NFD_ERROR, 16 | NFD_OKAY, 17 | NFD_CANCEL 18 | } 19 | 20 | public static class NativeFunctions 21 | { 22 | public const string LibraryName = "nfd"; 23 | 24 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 25 | public static extern unsafe nfdresult_t NFD_OpenDialog(byte* filterList, byte* defaultPath, out IntPtr outPath); 26 | 27 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 28 | public static extern unsafe nfdresult_t NFD_OpenDialogMultiple(byte* filterList, byte* defaultPath, 29 | nfdpathset_t* outPaths); 30 | 31 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 32 | public static extern unsafe nfdresult_t NFD_SaveDialog(byte* filterList, byte* defaultPath, out IntPtr outPath); 33 | 34 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 35 | public static extern unsafe nfdresult_t NFD_PickFolder(byte* defaultPath, out IntPtr outPath); 36 | 37 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 38 | public static extern unsafe byte* NFD_GetError(); 39 | 40 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 41 | public static extern unsafe UIntPtr NFD_PathSet_GetCount(nfdpathset_t* pathSet); 42 | 43 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 44 | public static extern unsafe byte* NFD_PathSet_GetPath(nfdpathset_t* pathSet, UIntPtr index); 45 | 46 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 47 | public static extern unsafe void NFD_PathSet_Free(nfdpathset_t* pathSet); 48 | 49 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 50 | public static extern unsafe void NFD_Dummy(); 51 | 52 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 53 | public static extern unsafe IntPtr NFD_Malloc(UIntPtr bytes); 54 | 55 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 56 | public static extern unsafe void NFD_Free(IntPtr ptr); 57 | } 58 | 59 | public static class NativeFunctions32 60 | { 61 | public const string LibraryName = "nfd_x86"; 62 | 63 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 64 | public static extern unsafe nfdresult_t NFD_OpenDialog(byte* filterList, byte* defaultPath, out IntPtr outPath); 65 | 66 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 67 | public static extern unsafe nfdresult_t NFD_OpenDialogMultiple(byte* filterList, byte* defaultPath, 68 | nfdpathset_t* outPaths); 69 | 70 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 71 | public static extern unsafe nfdresult_t NFD_SaveDialog(byte* filterList, byte* defaultPath, out IntPtr outPath); 72 | 73 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 74 | public static extern unsafe nfdresult_t NFD_PickFolder(byte* defaultPath, out IntPtr outPath); 75 | 76 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 77 | public static extern unsafe byte* NFD_GetError(); 78 | 79 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 80 | public static extern unsafe UIntPtr NFD_PathSet_GetCount(nfdpathset_t* pathSet); 81 | 82 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 83 | public static extern unsafe byte* NFD_PathSet_GetPath(nfdpathset_t* pathSet, UIntPtr index); 84 | 85 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 86 | public static extern unsafe void NFD_PathSet_Free(nfdpathset_t* pathSet); 87 | 88 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 89 | public static extern unsafe void NFD_Dummy(); 90 | 91 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 92 | public static extern unsafe IntPtr NFD_Malloc(UIntPtr bytes); 93 | 94 | [DllImport(LibraryName, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)] 95 | public static extern unsafe void NFD_Free(IntPtr ptr); 96 | } 97 | } -------------------------------------------------------------------------------- /NativeFileDialogSharp/NativeWrappers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Runtime.InteropServices; 5 | using System.Text; 6 | using NativeFileDialogSharp.Native; 7 | 8 | namespace NativeFileDialogSharp 9 | { 10 | public static class Dialog 11 | { 12 | private static readonly Encoder utf8encoder = Encoding.UTF8.GetEncoder(); 13 | 14 | private static readonly bool need32bit = Is32BitWindowsOnNetFramework(); 15 | 16 | private static bool Is32BitWindowsOnNetFramework() 17 | { 18 | try 19 | { 20 | // we call a function that does nothing just to test if we can load it properly 21 | NativeFileDialogSharp.Native.NativeFunctions.NFD_Dummy(); 22 | return false; 23 | } 24 | catch 25 | { 26 | // a call to a default library failed, let's attempt the other one 27 | try 28 | { 29 | NativeFileDialogSharp.Native.NativeFunctions32.NFD_Dummy(); 30 | return true; 31 | } 32 | catch 33 | { 34 | // both of them failed so we may as well default to the default one for predictability 35 | return false; 36 | } 37 | } 38 | } 39 | 40 | private static unsafe byte[] ToUtf8(string s) 41 | { 42 | var byteCount = Encoding.UTF8.GetByteCount(s); 43 | var bytes = new byte[byteCount + 1]; 44 | fixed (byte* o = bytes) 45 | fixed (char* input = s) 46 | { 47 | utf8encoder.Convert(input, s.Length, o, bytes.Length, true, out _, out var _, 48 | out var completed); 49 | Debug.Assert(completed); 50 | } 51 | 52 | return bytes; 53 | } 54 | 55 | private static unsafe int GetNullTerminatedStringLength(byte* nullTerminatedString) 56 | { 57 | int count = 0; 58 | var ptr = nullTerminatedString; 59 | while (*ptr != 0) 60 | { 61 | ptr++; 62 | count++; 63 | } 64 | 65 | return count; 66 | } 67 | 68 | private static unsafe string FromUtf8(byte* nullTerminatedString) 69 | { 70 | return Encoding.UTF8.GetString(nullTerminatedString, GetNullTerminatedStringLength(nullTerminatedString)); 71 | } 72 | 73 | public static unsafe DialogResult FileOpen(string filterList = null, string defaultPath = null) 74 | { 75 | fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null) 76 | fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null) 77 | { 78 | string path = null; 79 | string errorMessage = null; 80 | IntPtr outPathIntPtr; 81 | var result = need32bit 82 | ? NativeFunctions32.NFD_OpenDialog(filterListNts, defaultPathNts, out outPathIntPtr) 83 | : NativeFunctions.NFD_OpenDialog(filterListNts, defaultPathNts, out outPathIntPtr); 84 | if (result == nfdresult_t.NFD_ERROR) 85 | { 86 | errorMessage = FromUtf8(NativeFunctions.NFD_GetError()); 87 | } 88 | else if (result == nfdresult_t.NFD_OKAY) 89 | { 90 | var outPathNts = (byte*)outPathIntPtr.ToPointer(); 91 | path = FromUtf8(outPathNts); 92 | NativeFunctions.NFD_Free(outPathIntPtr); 93 | } 94 | 95 | return new DialogResult(result, path, null, errorMessage); 96 | } 97 | } 98 | 99 | public static unsafe DialogResult FileSave(string filterList = null, string defaultPath = null) 100 | { 101 | fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null) 102 | fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null) 103 | { 104 | string path = null; 105 | string errorMessage = null; 106 | IntPtr outPathIntPtr; 107 | var result = need32bit 108 | ? NativeFunctions32.NFD_SaveDialog(filterListNts, defaultPathNts, out outPathIntPtr) 109 | : NativeFunctions.NFD_SaveDialog(filterListNts, defaultPathNts, out outPathIntPtr); 110 | if (result == nfdresult_t.NFD_ERROR) 111 | { 112 | errorMessage = FromUtf8(NativeFunctions.NFD_GetError()); 113 | } 114 | else if (result == nfdresult_t.NFD_OKAY) 115 | { 116 | var outPathNts = (byte*)outPathIntPtr.ToPointer(); 117 | path = FromUtf8(outPathNts); 118 | NativeFunctions.NFD_Free(outPathIntPtr); 119 | } 120 | 121 | return new DialogResult(result, path, null, errorMessage); 122 | } 123 | } 124 | 125 | public static unsafe DialogResult FolderPicker(string defaultPath = null) 126 | { 127 | fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null) 128 | { 129 | string path = null; 130 | string errorMessage = null; 131 | IntPtr outPathIntPtr; 132 | var result = need32bit 133 | ? NativeFunctions32.NFD_PickFolder(defaultPathNts, out outPathIntPtr) 134 | : NativeFunctions.NFD_PickFolder(defaultPathNts, out outPathIntPtr); 135 | if (result == nfdresult_t.NFD_ERROR) 136 | { 137 | errorMessage = FromUtf8(NativeFunctions.NFD_GetError()); 138 | } 139 | else if (result == nfdresult_t.NFD_OKAY) 140 | { 141 | var outPathNts = (byte*)outPathIntPtr.ToPointer(); 142 | path = FromUtf8(outPathNts); 143 | NativeFunctions.NFD_Free(outPathIntPtr); 144 | } 145 | 146 | return new DialogResult(result, path, null, errorMessage); 147 | } 148 | } 149 | 150 | public static unsafe DialogResult FileOpenMultiple(string filterList = null, string defaultPath = null) 151 | { 152 | fixed (byte* filterListNts = filterList != null ? ToUtf8(filterList) : null) 153 | fixed (byte* defaultPathNts = defaultPath != null ? ToUtf8(defaultPath) : null) 154 | { 155 | List paths = null; 156 | string errorMessage = null; 157 | nfdpathset_t pathSet; 158 | var result = need32bit 159 | ? NativeFunctions32.NFD_OpenDialogMultiple(filterListNts, defaultPathNts, &pathSet) 160 | : NativeFunctions.NFD_OpenDialogMultiple(filterListNts, defaultPathNts, &pathSet); 161 | if (result == nfdresult_t.NFD_ERROR) 162 | { 163 | errorMessage = FromUtf8(NativeFunctions.NFD_GetError()); 164 | } 165 | else if (result == nfdresult_t.NFD_OKAY) 166 | { 167 | var pathCount = (int)NativeFunctions.NFD_PathSet_GetCount(&pathSet).ToUInt32(); 168 | paths = new List(pathCount); 169 | for (int i = 0; i < pathCount; i++) 170 | { 171 | paths.Add(FromUtf8(NativeFunctions.NFD_PathSet_GetPath(&pathSet, new UIntPtr((uint)i)))); 172 | } 173 | 174 | NativeFunctions.NFD_PathSet_Free(&pathSet); 175 | } 176 | 177 | return new DialogResult(result, null, paths, errorMessage); 178 | } 179 | } 180 | } 181 | 182 | public class DialogResult 183 | { 184 | private readonly nfdresult_t result; 185 | 186 | public string Path { get; } 187 | 188 | public IReadOnlyList Paths { get; } 189 | 190 | public bool IsError => result == nfdresult_t.NFD_ERROR; 191 | 192 | public string ErrorMessage { get; } 193 | 194 | public bool IsCancelled => result == nfdresult_t.NFD_CANCEL; 195 | 196 | public bool IsOk => result == nfdresult_t.NFD_OKAY; 197 | 198 | internal DialogResult(nfdresult_t result, string path, IReadOnlyList paths, string errorMessage) 199 | { 200 | this.result = result; 201 | Path = path; 202 | Paths = paths; 203 | ErrorMessage = errorMessage; 204 | } 205 | } 206 | } --------------------------------------------------------------------------------