// This type must be kept in sync with the version in C. public ApiFunctions(ApiFunctionPointers p) { // Data XPLMFindDataRef = Marshal.GetDelegateForFunctionPointer <XPLMFindDataRef>(p.XPLMFindDataRef); XPLMGetDataRefTypes = Marshal.GetDelegateForFunctionPointer <XPLMGetDataRefTypes>(p.XPLMGetDataRefTypes); XPLMGetDatai = Marshal.GetDelegateForFunctionPointer <XPLMGetDatai>(p.XPLMGetDatai); XPLMSetDatai = Marshal.GetDelegateForFunctionPointer <XPLMSetDatai>(p.XPLMSetDatai); XPLMGetDataf = Marshal.GetDelegateForFunctionPointer <XPLMGetDataf>(p.XPLMGetDataf); XPLMSetDataf = Marshal.GetDelegateForFunctionPointer <XPLMSetDataf>(p.XPLMSetDataf); XPLMGetDatad = Marshal.GetDelegateForFunctionPointer <XPLMGetDatad>(p.XPLMGetDatad); XPLMSetDatad = Marshal.GetDelegateForFunctionPointer <XPLMSetDatad>(p.XPLMSetDatad); XPLMGetDatavi = Marshal.GetDelegateForFunctionPointer <XPLMGetDatavi>(p.XPLMGetDatavi); XPLMSetDatavi = Marshal.GetDelegateForFunctionPointer <XPLMSetDatavi>(p.XPLMSetDatavi); XPLMGetDatavf = Marshal.GetDelegateForFunctionPointer <XPLMGetDatavf>(p.XPLMGetDatavf); XPLMSetDatavf = Marshal.GetDelegateForFunctionPointer <XPLMSetDatavf>(p.XPLMSetDatavf); XPLMGetDatab = Marshal.GetDelegateForFunctionPointer <XPLMGetDatab>(p.XPLMGetDatab); XPLMSetDatab = Marshal.GetDelegateForFunctionPointer <XPLMSetDatab>(p.XPLMSetDatab); // Commands XPLMFindCommand = Marshal.GetDelegateForFunctionPointer <XPLMFindCommand>(p.XPLMFindCommand); XPLMCommandBegin = Marshal.GetDelegateForFunctionPointer <XPLMCommandBegin>(p.XPLMCommandBegin); XPLMCommandEnd = Marshal.GetDelegateForFunctionPointer <XPLMCommandEnd>(p.XPLMCommandEnd); XPLMCommandOnce = Marshal.GetDelegateForFunctionPointer <XPLMCommandOnce>(p.XPLMCommandOnce); // Processing XPLMGetElapsedTime = Marshal.GetDelegateForFunctionPointer <XPLMGetElapsedTime>(p.XPLMGetElapsedTime); XPLMGetCycleNumber = Marshal.GetDelegateForFunctionPointer <XPLMGetCycleNumber>(p.XPLMGetCycleNumber); XPLMRegisterFlightLoopCallback = Marshal.GetDelegateForFunctionPointer <XPLMRegisterFlightLoopCallback>(p.XPLMRegisterFlightLoopCallback); XPLMUnregisterFlightLoopCallback = Marshal.GetDelegateForFunctionPointer <XPLMUnregisterFlightLoopCallback>(p.XPLMUnregisterFlightLoopCallback); XPLMSetFlightLoopCallbackInterval = Marshal.GetDelegateForFunctionPointer <XPLMSetFlightLoopCallbackInterval>(p.XPLMSetFlightLoopCallbackInterval); XPLMCreateFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMCreateFlightLoop>(p.XPLMCreateFlightLoop); XPLMDestroyFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMDestroyFlightLoop>(p.XPLMDestroyFlightLoop); XPLMScheduleFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMScheduleFlightLoop>(p.XPLMScheduleFlightLoop); }
// This type must be kept in sync with the version in C. public ApiFunctions(ApiFunctionPointers p) { // Data XPLMFindDataRef = Marshal.GetDelegateForFunctionPointer <XPLMFindDataRef>(p.XPLMFindDataRef); XPLMGetDataRefTypes = Marshal.GetDelegateForFunctionPointer <XPLMGetDataRefTypes>(p.XPLMGetDataRefTypes); XPLMGetDatai = Marshal.GetDelegateForFunctionPointer <XPLMGetDatai>(p.XPLMGetDatai); XPLMSetDatai = Marshal.GetDelegateForFunctionPointer <XPLMSetDatai>(p.XPLMSetDatai); XPLMGetDataf = Marshal.GetDelegateForFunctionPointer <XPLMGetDataf>(p.XPLMGetDataf); XPLMSetDataf = Marshal.GetDelegateForFunctionPointer <XPLMSetDataf>(p.XPLMSetDataf); XPLMGetDatad = Marshal.GetDelegateForFunctionPointer <XPLMGetDatad>(p.XPLMGetDatad); XPLMSetDatad = Marshal.GetDelegateForFunctionPointer <XPLMSetDatad>(p.XPLMSetDatad); XPLMGetDatavi = Marshal.GetDelegateForFunctionPointer <XPLMGetDatavi>(p.XPLMGetDatavi); XPLMSetDatavi = Marshal.GetDelegateForFunctionPointer <XPLMSetDatavi>(p.XPLMSetDatavi); XPLMGetDatavf = Marshal.GetDelegateForFunctionPointer <XPLMGetDatavf>(p.XPLMGetDatavf); XPLMSetDatavf = Marshal.GetDelegateForFunctionPointer <XPLMSetDatavf>(p.XPLMSetDatavf); XPLMGetDatab = Marshal.GetDelegateForFunctionPointer <XPLMGetDatab>(p.XPLMGetDatab); XPLMSetDatab = Marshal.GetDelegateForFunctionPointer <XPLMSetDatab>(p.XPLMSetDatab); XPLMRegisterDataAccessor = Marshal.GetDelegateForFunctionPointer <XPLMRegisterDataAccessor>(p.XPLMRegisterDataAccessor); XPLMUnregisterDataAccessor = Marshal.GetDelegateForFunctionPointer <XPLMUnregisterDataAccessor>(p.XPLMUnregisterDataAccessor); // Commands XPLMFindCommand = Marshal.GetDelegateForFunctionPointer <XPLMFindCommand>(p.XPLMFindCommand); XPLMCommandBegin = Marshal.GetDelegateForFunctionPointer <XPLMCommandBegin>(p.XPLMCommandBegin); XPLMCommandEnd = Marshal.GetDelegateForFunctionPointer <XPLMCommandEnd>(p.XPLMCommandEnd); XPLMCommandOnce = Marshal.GetDelegateForFunctionPointer <XPLMCommandOnce>(p.XPLMCommandOnce); // Processing XPLMGetElapsedTime = Marshal.GetDelegateForFunctionPointer <XPLMGetElapsedTime>(p.XPLMGetElapsedTime); XPLMGetCycleNumber = Marshal.GetDelegateForFunctionPointer <XPLMGetCycleNumber>(p.XPLMGetCycleNumber); XPLMRegisterFlightLoopCallback = Marshal.GetDelegateForFunctionPointer <XPLMRegisterFlightLoopCallback>(p.XPLMRegisterFlightLoopCallback); XPLMUnregisterFlightLoopCallback = Marshal.GetDelegateForFunctionPointer <XPLMUnregisterFlightLoopCallback>(p.XPLMUnregisterFlightLoopCallback); XPLMSetFlightLoopCallbackInterval = Marshal.GetDelegateForFunctionPointer <XPLMSetFlightLoopCallbackInterval>(p.XPLMSetFlightLoopCallbackInterval); XPLMCreateFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMCreateFlightLoop>(p.XPLMCreateFlightLoop); XPLMDestroyFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMDestroyFlightLoop>(p.XPLMDestroyFlightLoop); XPLMScheduleFlightLoop = Marshal.GetDelegateForFunctionPointer <XPLMScheduleFlightLoop>(p.XPLMScheduleFlightLoop); // Display XPLMRegisterDrawCallback = Marshal.GetDelegateForFunctionPointer <XPLMRegisterDrawCallback>(p.XPLMRegisterDrawCallback); XPLMUnregisterDrawCallback = Marshal.GetDelegateForFunctionPointer <XPLMUnregisterDrawCallback>(p.XPLMUnregisterDrawCallback); // Scenery XPLMCreateProbe = Marshal.GetDelegateForFunctionPointer <XPLMCreateProbe>(p.XPLMCreateProbe); XPLMDestroyProbe = Marshal.GetDelegateForFunctionPointer <XPLMDestroyProbe>(p.XPLMDestroyProbe); XPLMProbeTerrainXYZ = Marshal.GetDelegateForFunctionPointer <XPLMProbeTerrainXYZ>(p.XPLMProbeTerrainXYZ); XPLMLoadObject = Marshal.GetDelegateForFunctionPointer <XPLMLoadObject>(p.XPLMLoadObject); XPLMLoadObjectAsync = Marshal.GetDelegateForFunctionPointer <XPLMLoadObjectAsync>(p.XPLMLoadObjectAsync); XPLMDrawObjects = Marshal.GetDelegateForFunctionPointer <XPLMDrawObjects>(p.XPLMDrawObjects); XPLMUnloadObject = Marshal.GetDelegateForFunctionPointer <XPLMUnloadObject>(p.XPLMUnloadObject); XPLMLookupObjects = Marshal.GetDelegateForFunctionPointer <XPLMLookupObjects>(p.XPLMLookupObjects); // Graphics XPLMWorldToLocal = Marshal.GetDelegateForFunctionPointer <XPLMWorldToLocal>(p.XPLMWorldToLocal); XPLMLocalToWorld = Marshal.GetDelegateForFunctionPointer <XPLMLocalToWorld>(p.XPLMLocalToWorld); // InstanceDrawing XPLMCreateInstance = Marshal.GetDelegateForFunctionPointer <XPLMCreateInstance>(p.XPLMCreateInstance); XPLMDestroyInstance = Marshal.GetDelegateForFunctionPointer <XPLMDestroyInstance>(p.XPLMDestroyInstance); XPLMInstanceSetPosition = Marshal.GetDelegateForFunctionPointer <XPLMInstanceSetPosition>(p.XPLMInstanceSetPosition); }
internal static bool StartInternal(ref StartParams startParams, ref ApiFunctionPointers apiFunctionPointers, string rootDir) { if (!File.Exists(m_configFilePath)) { m_log.Log($"XPNet CLR: Will not load plugin because config file does not exist: (Path = {m_configFilePath})."); return(false); } m_config = GetConfig(m_configFilePath); if (m_config == null) { m_log.Log($"XPNet CLR: Will not load plugin because config file was unusable: (Path = {m_configFilePath})."); return(false); } m_configReloadToken = m_config.GetReloadToken(); m_configReloadTokenDisposer = m_configReloadToken.RegisterChangeCallback(o => { m_log.Log($"XPNet CLR: Config file change detected: (Path = {m_configFilePath})."); m_log.Log($"XPNet CLR: Will reconfigure logging."); m_api.Log = m_log = ReconfigureLogging(forceLogging: false); m_log.Log($"XPNet CLR: Will tell plugin that config changed."); m_api.RaiseConfigChanged(); }, state: null); // Make a local copy of the given set of API function pointers. ApiFunctions = new ApiFunctions(apiFunctionPointers); m_api = new XPlaneApi(m_log, m_config); m_plugin = LoadPlugin(rootDir); if (m_plugin == null) { m_log.Log("XPNet CLR: Failed to find a plugin to load. Will tell X-Plane we failed to start."); return(false); } var typeInfo = m_plugin.GetType().GetTypeInfo(); var xpattr = typeInfo.GetCustomAttribute <XPlanePluginAttribute>(); unsafe { fixed(byte *pc = startParams.Name) Interop.CopyCString(pc, 256, xpattr.Name); fixed(byte *pc = startParams.Signature) Interop.CopyCString(pc, 256, xpattr.Signature); fixed(byte *pc = startParams.Description) Interop.CopyCString(pc, 256, xpattr.Description); } return(true); }
public static bool Start(ref StartParams startParams, ref ApiFunctionPointers apiFunctionPointers) { // MAINT: This is the initial entry point that gets called from C++ when the plugin system is // initialized. // // * Do not call anything from _this_ method that requires any other DLLs, other than those // that come with the framework itself. (Put such calls into StartInternal instead). // // * Do not introduce anything in a static constructor of this class that requires any // other DLLs, other than those that come with the framework itself. // // Violating either of those rules makes debugging "missing dependency" problems much more // difficult. When the framework finds a missing dependency, it throws an exception. If that // happens at a place where we cannot catch it in C#, then it will make it back to C++-land // as a hard crash. (It is actually an NT exception and could be handled with __try/__except, // but that wouldn't work on other macOS or Linux, and we wouldn't be able to log any information // about the exception, so that's a non-optimal solution). // // The reason you can't call methods that are loaded from other DLLs here, but can in StartInternal, // is to do with how the JIT compiler works. If it encounters a method it can't compile because // of a missing DLL, it will fail to compile _that_ method with an exception. If one of the core // dependencies (like Microsoft.Configuration.*) is missing, we want that failure to occur while // compiling StartInternal, which we call from here. That way, we can catch it with our try/catch // here, and report a good error message. If _this_ method were to fail to JIT-compile, there are // no other .NET methods higher in the stack to catch and properly report the error, and the result // looks to the user like a hard crash. // // Logically, this method should be very small, just a try/catch wrapper around StartInternal. // However, // // 1. Logging is safe, because we specifically make sure that it doesn't require any external // dependencies. (That is one of the reasons that we aren't using something like log4net). // 2. Setting up logging (but nothing else!) at this level lets us pass back to the host environment // a useful message about exactly which DLL is missing, because in practice the host environment // is our C++ X-Plane plugin DLL, which passes a log handle, so it can catch what we log. // Without that, we might as well just be telling the user "¯\_(ツ)_/¯ sucks to be you..." // // NOTE: There's not much we can do right now about unhandled exceptions on other threads. // Plugin writers will have to be conscientious about taking care of that themselves. When/if // .NET Core adds an equivalent to AppDomain.UnhandledException, we could give it a try. // try { var thisAssemblyPath = typeof(PluginBridge).GetTypeInfo().Assembly.Location; var rootDir = Path.GetDirectoryName(thisAssemblyPath); m_configFilePath = Path.Combine(rootDir, "xpnetcfg.json"); m_logFilePath = Path.Combine(rootDir, "xpnet.log"); // We always start out with logging enabled. We may reconfigure it off once config loads. m_log = InitLogging(ref startParams); m_log.Log("XPNet CLR: Start"); return(StartInternal(ref startParams, ref apiFunctionPointers, rootDir)); } catch (Exception exc) { // File.WriteAllText(@"D:\Games\X-Plane\X-Plane 11 Beta\Resources\plugins\XPNetDev\64\Emergency.txt", "Error: " + exc); m_log?.Log(exc); return(false); } }
public static bool Start(ref StartParams startParams, ref ApiFunctionPointers apiFunctionPointers) { try { var thisAssemblyPath = typeof(PluginBridge).GetTypeInfo().Assembly.Location; var rootDir = Path.GetDirectoryName(thisAssemblyPath); m_configFilePath = Path.Combine(rootDir, "xpnetcfg.json"); m_logFilePath = Path.Combine(rootDir, "xpnet.log"); // We always start out with logging enabled. We may reconfigure it off once config loads. m_log = InitLogging(ref startParams); m_log.Log("XPNet CLR: Start"); if (!File.Exists(m_configFilePath)) { m_log.Log($"XPNet CLR: Will not load plugin because config file does not exist: (Path = {m_configFilePath})."); return(false); } m_config = GetConfig(m_configFilePath); if (m_config == null) { m_log.Log($"XPNet CLR: Will not load plugin because config file was unusable: (Path = {m_configFilePath})."); return(false); } m_configReloadToken = m_config.GetReloadToken(); m_configReloadTokenDisposer = m_configReloadToken.RegisterChangeCallback(o => { m_log.Log($"XPNet CLR: Config file change detected: (Path = {m_configFilePath})."); m_log.Log($"XPNet CLR: Will reconfigure logging."); m_api.Log = m_log = ReconfigureLogging(forceLogging: false); m_log.Log($"XPNet CLR: Will tell plugin that config changed."); m_api.RaiseConfigChanged(); }, state: null); // Make a local copy of the given set of API function pointers. ApiFunctions = new ApiFunctions(apiFunctionPointers); m_api = new XPlaneApi(m_log, m_config); m_plugin = LoadPlugin(rootDir); if (m_plugin == null) { m_log.Log("XPNet CLR: Failed to find a plugin to load. Will tell X-Plane we failed to start."); return(false); } var typeInfo = m_plugin.GetType().GetTypeInfo(); var xpattr = typeInfo.GetCustomAttribute <XPlanePluginAttribute>(); unsafe { fixed(byte *pc = startParams.Name) Interop.CopyCString(pc, 256, xpattr.Name); fixed(byte *pc = startParams.Signature) Interop.CopyCString(pc, 256, xpattr.Signature); fixed(byte *pc = startParams.Description) Interop.CopyCString(pc, 256, xpattr.Description); } return(true); } catch (Exception exc) { // File.WriteAllText(@"D:\Games\X-Plane\X-Plane 11 Beta\Resources\plugins\XPNetDev\64\Emergency.txt", "Error: " + exc); m_log?.Log(exc); return(false); } }