-
Notifications
You must be signed in to change notification settings - Fork 508
ComImportAttribute doesn't seem to work #6252
Comments
The problem is tracked by #4219. CoreRT does not support COM interop currently and it is unlikely to change anytime soon. If you need to do a little bit of COM interop, you can workaround this by doing the COM interop manually. I have attached what it looks like for your code sample. If you need to do a lot of COM interop, you may take a look at https://github.com/SharpGenTools/SharpGenTools to generate the COM interop marshaling code. The code generated by this tool should be compatible with CoreRT. using System;
using System.Runtime.InteropServices;
namespace MyNamespace
{
class Program
{
static void Main()
{
new FolderBrowser2().ShowDialog(null);
}
}
public enum DialogResult
{
Cancel, Abort, OK
}
public class FolderBrowser2
{
public string DirectoryPath { get; set; }
public DialogResult ShowDialog(IntPtr? owner)
{
IntPtr hwndOwner = owner ?? GetActiveWindow();
using (var dialog = FileOpenDialog.Create())
{
if (!string.IsNullOrEmpty(DirectoryPath))
{
IntPtr idl;
uint atts = 0;
if (SHILCreateFromPath(DirectoryPath, out idl, ref atts) == 0)
{
using (var item = new ShellItem())
{
if (SHCreateShellItem(IntPtr.Zero, IntPtr.Zero, idl, out item.Pointer) == 0)
{
dialog.SetFolder(item);
}
}
Marshal.FreeCoTaskMem(idl);
}
}
dialog.SetOptions(FOS.FOS_PICKFOLDERS | FOS.FOS_FORCEFILESYSTEM);
uint hr = dialog.Show(hwndOwner);
if (hr == ERROR_CANCELLED)
return DialogResult.Cancel;
if (hr != 0)
return DialogResult.Abort;
using (var item = dialog.GetResult())
{
string path;
item.GetDisplayName(SIGDN.SIGDN_FILESYSPATH, out path);
DirectoryPath = path;
}
return DialogResult.OK;
}
}
[DllImport("ole32.dll")]
private static extern int CoInitializeEx(IntPtr pvReserved, int dwCoInit);
[DllImport("shell32.dll")]
private static extern int SHILCreateFromPath([MarshalAs(UnmanagedType.LPWStr)] string pszPath, out IntPtr ppIdl, ref uint rgflnOut);
[DllImport("shell32.dll")]
private static extern int SHCreateShellItem(IntPtr pidlParent, IntPtr psfParent, IntPtr pidl, out IntPtr ppsi);
[DllImport("user32.dll")]
private static extern IntPtr GetActiveWindow();
private const uint ERROR_CANCELLED = 0x800704C7;
private unsafe class MiniComObject : IDisposable
{
public IntPtr Pointer;
delegate int IUnknown_Release(IntPtr thisPtr);
public void Dispose()
{
Marshal.GetDelegateForFunctionPointer<IUnknown_Release>(*((*(IntPtr**)Pointer) + 2))(Pointer);
Pointer = IntPtr.Zero;
}
}
private unsafe class FileOpenDialog : MiniComObject
{
[DllImport("ole32.dll")]
private static extern int CoCreateInstance(ref Guid rclsid, IntPtr pUnkOuter,
Int32 dwClsContext,
ref Guid riid,
out IntPtr ppv);
public static FileOpenDialog Create()
{
Marshal.ThrowExceptionForHR(CoInitializeEx(IntPtr.Zero, 0 /* COINIT_APARTMENTTHREADED */), new IntPtr(-1));
FileOpenDialog result = new FileOpenDialog();
Guid CLSID_FileOpenDialog = new Guid("DC1C5A9C-E88A-4dde-A5A1-60F82A20AEF7");
Guid IID_IFileOpenDialog = new Guid("42f85136-db7e-439c-85f1-e4075d135fc8");
int hr = CoCreateInstance(ref CLSID_FileOpenDialog, IntPtr.Zero,
1, // CLSCTX_INPROC_SERVER,
ref IID_IFileOpenDialog,
out result.Pointer);
Marshal.ThrowExceptionForHR(hr, new IntPtr(-1));
return result;
}
delegate uint IFileOpenDialog_Show(IntPtr thisPtr, IntPtr parent);
public uint Show([In] IntPtr parent)
{
return Marshal.GetDelegateForFunctionPointer<IFileOpenDialog_Show>(*((*(IntPtr**)Pointer) + 3))(Pointer, parent);
}
delegate int IFileOpenDialog_SetOptions(IntPtr thisPtr, FOS fos);
public void SetOptions(FOS fos)
{
Marshal.ThrowExceptionForHR(Marshal.GetDelegateForFunctionPointer<IFileOpenDialog_SetOptions>(*((*(IntPtr**)Pointer) + 9))(Pointer, fos), new IntPtr(-1));
}
delegate int IFileOpenDialog_SetFolder(IntPtr thisPtr, IntPtr psi);
public void SetFolder(ShellItem psi)
{
Marshal.ThrowExceptionForHR(Marshal.GetDelegateForFunctionPointer<IFileOpenDialog_SetFolder>(*((*(IntPtr**)Pointer) + 12))(Pointer, psi.Pointer), new IntPtr(-1));
}
delegate int IFileOpenDialog_GetResult(IntPtr thisPtr, out IntPtr ppsi);
public ShellItem GetResult()
{
var result = new ShellItem();
Marshal.ThrowExceptionForHR(Marshal.GetDelegateForFunctionPointer<IFileOpenDialog_GetResult>(*((*(IntPtr**)Pointer) + 20))(Pointer, out result.Pointer), new IntPtr(-1));
return result;
}
}
private unsafe class ShellItem : MiniComObject
{
delegate int IShellItem_GetDisplayName(IntPtr thisPtr, SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
public void GetDisplayName(SIGDN sigdnName, out string ppszName)
{
Marshal.ThrowExceptionForHR(Marshal.GetDelegateForFunctionPointer<IShellItem_GetDisplayName>(*((*(IntPtr**)Pointer) + 5))(Pointer, sigdnName, out ppszName), new IntPtr(-1));
}
}
#if false
[ComImport]
[Guid("42f85136-db7e-439c-85f1-e4075d135fc8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IFileOpenDialog
{
[PreserveSig]
uint Show([In] IntPtr parent); // IModalWindow
void SetFileTypes(); // not fully defined
void SetFileTypeIndex([In] uint iFileType);
void GetFileTypeIndex(out uint piFileType);
void Advise(); // not fully defined
void Unadvise();
void SetOptions([In] FOS fos);
void GetOptions(out FOS pfos);
void SetDefaultFolder(IShellItem psi);
void SetFolder(IShellItem psi);
void GetFolder(out IShellItem ppsi);
void GetCurrentSelection(out IShellItem ppsi);
void SetFileName([In, MarshalAs(UnmanagedType.LPWStr)] string pszName);
void GetFileName([MarshalAs(UnmanagedType.LPWStr)] out string pszName);
void SetTitle([In, MarshalAs(UnmanagedType.LPWStr)] string pszTitle);
void SetOkButtonLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszText);
void SetFileNameLabel([In, MarshalAs(UnmanagedType.LPWStr)] string pszLabel);
void GetResult(out IShellItem ppsi);
void AddPlace(IShellItem psi, int alignment);
void SetDefaultExtension([In, MarshalAs(UnmanagedType.LPWStr)] string pszDefaultExtension);
void Close(int hr);
void SetClientGuid(); // not fully defined
void ClearClientData();
void SetFilter([MarshalAs(UnmanagedType.Interface)] IntPtr pFilter);
void GetResults([MarshalAs(UnmanagedType.Interface)] out IntPtr ppenum); // not fully defined
void GetSelectedItems([MarshalAs(UnmanagedType.Interface)] out IntPtr ppsai); // not fully defined
}
[ComImport]
[Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IShellItem
{
void BindToHandler(); // not fully defined
void GetParent(); // not fully defined
void GetDisplayName([In] SIGDN sigdnName, [MarshalAs(UnmanagedType.LPWStr)] out string ppszName);
void GetAttributes(); // not fully defined
void Compare(); // not fully defined
}
#endif
private enum SIGDN : uint
{
SIGDN_DESKTOPABSOLUTEEDITING = 0x8004c000,
SIGDN_DESKTOPABSOLUTEPARSING = 0x80028000,
SIGDN_FILESYSPATH = 0x80058000,
SIGDN_NORMALDISPLAY = 0,
SIGDN_PARENTRELATIVE = 0x80080001,
SIGDN_PARENTRELATIVEEDITING = 0x80031001,
SIGDN_PARENTRELATIVEFORADDRESSBAR = 0x8007c001,
SIGDN_PARENTRELATIVEPARSING = 0x80018001,
SIGDN_URL = 0x80068000
}
[Flags]
private enum FOS
{
FOS_ALLNONSTORAGEITEMS = 0x80,
FOS_ALLOWMULTISELECT = 0x200,
FOS_CREATEPROMPT = 0x2000,
FOS_DEFAULTNOMINIMODE = 0x20000000,
FOS_DONTADDTORECENT = 0x2000000,
FOS_FILEMUSTEXIST = 0x1000,
FOS_FORCEFILESYSTEM = 0x40,
FOS_FORCESHOWHIDDEN = 0x10000000,
FOS_HIDEMRUPLACES = 0x20000,
FOS_HIDEPINNEDPLACES = 0x40000,
FOS_NOCHANGEDIR = 8,
FOS_NODEREFERENCELINKS = 0x100000,
FOS_NOREADONLYRETURN = 0x8000,
FOS_NOTESTFILECREATE = 0x10000,
FOS_NOVALIDATE = 0x100,
FOS_OVERWRITEPROMPT = 2,
FOS_PATHMUSTEXIST = 0x800,
FOS_PICKFOLDERS = 0x20,
FOS_SHAREAWARE = 0x4000,
FOS_STRICTFILETYPES = 4
}
}
} |
Hi, I tested the provided sample in a netcore 3.0 console app and it worked only for opening the Dialog with the Show method, but failed at GetResult with an AccessViolationException. I might have fixed it by changing the pointer offset from +12 to +20... but then it crashes at GetDisplayName, which for me looks like is completly ignoring the given SIGDN sigdnName. I am not sure how the offset is calculated. to me it looks like first member is at base +3. @jkotas Can you please provide a guide on how you calculated the pointer offsets? I tried to build SharpGenTools but the build fails during header parsing of ShObjIdl_core.h (...) and there is nowhere a working sample of SharpGenTools to be found. |
That's the right fix. Thanks for fixing it.
The
Maybe open an issue on this in SharpGenTools repo. cc @jkoritzinsky |
Hi, this project looks really great and is exactly what I need.
I want to create a simple internal tool. This is mostly a console application, but for added convenience, it should be minimally interactive when no parameters are passed to it.
That means MessageBoxes and OpenFileDialogs to gather the information from the user.
Without .NET Core 3, I don't have access to WinForms, so I thought I'd just p/invoke it.
pinvoking MessageBox works good, but I have issues with the Vista File Dialogs, which use COM.
code was adapted from https://stackoverflow.com/a/15386992/1872399.
This works in .NET Core 2.1, but doesn't work with CoreRT.
Compile Time
dotnet publish -r win-x64 -c release
Run Time
Used version
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="1.0.0-alpha-26820-01" />
.NET Sdk:
2.1.301
The text was updated successfully, but these errors were encountered: