#C | הטמעת ממשקי COM במצב NativeAOT
-
ב - 8 NET. שהושק לפני כמה ימים, במיקרוסופט פיתחו בין היתר את התכונה Native-AOT, למי שלא מכיר זה מאפשר לקמפל קוד #C לקוד מכונה מקורי, ללא קוד IL ובלי שימוש במנגנון JIT. יש בזה הרבה מעלות (אין צורך בזמן ריצה מותקן, זמן טעינה מהיר, ועוד) אבל יש גם קצת מגבלות, כרגיל.
אחת הבעיות היא הטמעת ממשקי COM בקוד, ראיתי שיש הרבה דיבור על זה ברשת, אבל בתכל'ס אני לא מצליח ליישם את זה.אני לוקח לדוגמא את הקוד הבא:
using System.Runtime.InteropServices; using static Taskbar; try { var taskbar = (ITaskbarList3)new TaskBarCommunication(); Console.WriteLine("COM interface created successfully"); } catch (Exception exception) { Console.WriteLine($"Error at creating COM interface: {exception}"); } Console.ReadLine(); static class Taskbar { [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF")] public interface ITaskbarList3 { // ITaskbarList [PreserveSig] void HrInit(); [PreserveSig] void AddTab(IntPtr hwnd); [PreserveSig] void DeleteTab(IntPtr hwnd); [PreserveSig] void ActivateTab(IntPtr hwnd); [PreserveSig] void SetActiveAlt(IntPtr hwnd); // ITaskbarList2 [PreserveSig] void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); // ITaskbarList3 [PreserveSig] void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); [PreserveSig] void SetProgressState(IntPtr hwnd, int state); } [ComImport] [ClassInterface(ClassInterfaceType.None)] [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] public class TaskBarCommunication { } }
קובץ הפרויקט:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <PublishAot>true</PublishAot> <BuiltInComInteropSupport>true</BuiltInComInteropSupport> </PropertyGroup> </Project>
כשאני מריץ עם:
dotnet run
זה רץ בלי בעיות, אבל כשאני מקמפל עםdotnet publish
זה זורק את החריג הבא:System.InvalidProgramException: Common Language Runtime detected an invalid program. The body of method 'Void TaskBarCommunication..ctor()' is invalid. at Internal.Runtime.TypeLoaderExceptionHelper.CreateInvalidProgramException(ExceptionStringID, String) + 0x40 at Internal.Runtime.CompilerHelpers.ThrowHelpers.ThrowInvalidProgramExceptionWithArgument(ExceptionStringID, String) + 0x9 at ComWithNativeAotSample.Taskbar.TaskBarCommunication..ctor() + 0x15 at Program.<Main>$(String[] args) + 0x34
משהו יכול להסביר לי איך אני אמור לשנות את הקוד כדי שזה יעבוד עם native-aot?
-
-
בסוף אחד הקישורים הם מציינים בהקשר הזה שהם מתחילים להוסיף תמיכה לcode generator עבור רכיבי COM, ניסית לבדוק אם זה פותר את השגיאה?
https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#source-generated-com-interop -
@קומפיונט כתב ב#C | הטמעת ממשקי COM במצב NativeAOT:
@dovid אני כבר מכיר את הקישורים האלו, אבל עדין אני לא מבין מה אני צריך לעשות בשורה התחתונה.
אתה לא כ"כ מכיר כי כתוב שמה שהם לא מתכוננים לתת תמיכה לCOM interop נכון לעכשיו (וזה נכתב כבר ב2020 וזה עדיין בתוקף).
-
@dovid לא שמתי לב שזה כתוב שם, אבל אני יודע שאפשר ליישם ממשקי COM ב native-aot, בדקתי את קוד המקור של avalonia שתומכת ב native-aot באופן מלא, וראיתי שהם פיתחו תת פרויקט (MicroCom) שמחולל קוד לממשקי COM מקבצי idl. לא התעמקתי בזה כ"כ כי אני לא מכיר קבצי idl, בתכלס אני שואל מה הדרך הכי קלה ליישם ממשקי COM עם או בלי מיקרוסופט?
-
@חגי כתב ב#C | הטמעת ממשקי COM במצב NativeAOT:
בסוף אחד הקישורים הם מציינים בהקשר הזה שהם מתחילים להוסיף תמיכה לcode generator עבור רכיבי COM, ניסית לבדוק אם זה פותר את השגיאה?
https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#source-generated-com-interopניסיתי גם את זה ונשאר עם אותה שגיאה (זה מגנרט קוד, אבל ב-native-aot זה זורק אותו חריג), אולי לא ממשתי את זה נכון (יש דרך אחרת לקבל מופע של ממשק COM?)
static partial class Taskbar { [GeneratedComInterface] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [Guid("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF")] public partial interface ITaskbarList3 { // ITaskbarList [PreserveSig] void HrInit(); [PreserveSig] void AddTab(IntPtr hwnd); [PreserveSig] void DeleteTab(IntPtr hwnd); [PreserveSig] void ActivateTab(IntPtr hwnd); [PreserveSig] void SetActiveAlt(IntPtr hwnd); // ITaskbarList2 [PreserveSig] void MarkFullscreenWindow(IntPtr hwnd, [MarshalAs(UnmanagedType.Bool)] bool fFullscreen); // ITaskbarList3 [PreserveSig] void SetProgressValue(IntPtr hwnd, ulong ullCompleted, ulong ullTotal); [PreserveSig] void SetProgressState(IntPtr hwnd, int state); } [ComImport] [GeneratedComClass] [ClassInterface(ClassInterfaceType.None)] [Guid("56FDF344-FD6D-11d0-958A-006097C9A090")] public partial class TaskBarCommunication { } }
-
לאחר הרבה ניסיונות מצאתי בס"ד את הדרך ליישם את זה, אני שם את זה פה אם למישהו זה יהיה שימושי בעתיד.
א. הוספתי מחלקת עזר שתייצר ממשקי COM כדלהלן:
public static partial class ComActivator { [LibraryImport("ole32")] private static partial int CoCreateInstance( ref Guid clsid, nint pUnkOuter, int dwClsContext, ref Guid iid, [MarshalAs(UnmanagedType.Interface)] out object pObj ); public static TInterface ActivateClass<TInterface>(Guid clsid, Guid iid) { Debug.Assert(iid == typeof(TInterface).GUID); var hResult = CoCreateInstance(ref clsid, default, 1, ref iid, out var obj); if (hResult < 0) { Marshal.ThrowExceptionForHR(hResult); } return (TInterface)obj; } }
ב. למחלקה
ITaskbarList3
הוספתי את התכונהGeneratedComInterface
ג. ככה אני מקבל את האובייקט:
var clsid = Guid.Parse("56FDF344-FD6D-11D0-958A-006097C9A090"); // TaskBarList var iid = Guid.Parse("EA1AFB91-9E28-4B86-90E9-9E9F8A5EEFAF"); // ITaskBarList3 var taskbar = ComActivator.ActivateClass<ITaskbarList3>(clsid, iid);
קישור שעזר לי: https://learn.microsoft.com/en-us/dotnet/standard/native-interop/tutorial-comwrappers
נ.ב. מצאתי עוד פתרון עם
ComWrappers
אבל הפתרון הראשון עובד מצויין אז השתמשתי בו.