Nem Posted April 24, 2010 Share Posted April 24, 2010 I'm trying to update an old file format plug-in I wrote to work with the latest version of Paint.Net. Unfortunately, not mater what I try, I cannot get the plug-in to show up in Paint.Net. I've updated my project to 2008 and targeted 3.5 and the latest Paint.Net assemblies. The plug-in makes use of a C .dll, but removing all code references to the .dll does not change anything. I am unable to find any log files indicating what may be wrong. My source code can be found here: http://nemesis.thewavelength.net//files/pdnvtfplugin1xx-source.zip Any help would be appreciated! Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 24, 2010 Share Posted April 24, 2010 Have you tried reading the big sticky post in here titled "How to debug your plugin" ? ... Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 24, 2010 Author Share Posted April 24, 2010 I have; it odes not appear is if Paint.Net loads the .dll, however, the .dll is locked while Paint.Net is running, so it is finding it and doing something. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 25, 2010 Share Posted April 25, 2010 Set a breakpoint in your IFileTypeFactory constructor. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 25, 2010 Share Posted April 25, 2010 Your P/invokes are definitely defined wrong. You can't name the files VTFlib.dll.[x86/x64]. You need VTFlib.[x86/x64].dll, and then you need to implement appropriate P/Invoke dispatchers ("trampolines"). For example, from Paint.NET: (please excuse the indentation) (the "Processor" class is in PaintDotNet.SystemLayer, and is appropriate for plugins to use in this case) using namespace PaintDotNet.SystemLayer; internal static class PdnNativeMethods { // trampoline method to the "real" p/invokes -- this is the method that outside code uses internal static unsafe void MemCopy(void* dst, void* src, UIntPtr bytes) { if (Processor.Architecture == ProcessorArchitecture.X64) { x64.MemCopy(dst, src, bytes); } else { x86.MemCopy(dst, src, bytes); } } [suppressUnmanagedCodeSecurity] private static class x64 { [DllImport("PaintDotNet.Native.x64.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] internal static extern unsafe void MemSet(void* dst, int c, UIntPtr length); } [suppressUnmanagedCodeSecurity] private static class x86 { [DllImport("PaintDotNet.Native.x86.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = false)] internal static extern unsafe void MemSet(void* dst, int c, UIntPtr length); } Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 25, 2010 Author Share Posted April 25, 2010 Thanks for your help Rick. I just named the .dll with the x84/x64 extension for the .zip, I've been testing with the appropriate .dll for my system. However, as I mentioned before, if I completely remove all references to VTFLib from my code, the plug-in still does not work. (Thanks for the tip though; I'll use it in the final release.) I noticed today that Paint.Net does not attempt to load the file format plug-ins until you select open or save from the menu. When I do that I can now see that I am getting a System.Reflection.ReflectionTypeLoadException exception; however, I have no idea why. Is it possible to get Paint.Net to output more debug info? I get this exception even if I remove all references to VTFLib and make everything except for VTFFileTypes:IFileTypeFactory and VTFFileType:FileType internal. Quote Link to comment Share on other sites More sharing options...
pyrochild Posted April 25, 2010 Share Posted April 25, 2010 does the reflectiontypeloadexception provide any more details? Quote ambigram signature by Kemaru [i write plugins and stuff] If you like a post, upvote it! Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 25, 2010 Share Posted April 25, 2010 Yes, file type plugins aren't loaded until they are needed -- which isn't until the user requests an Open or Save dialog. Like I said, use the post that talks about debugging your plugin. You will need to look at the InnerException on that, or tell VS to break on any exception and not just unhandled ones. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 25, 2010 Author Share Posted April 25, 2010 Thanks you all your help! The callstack was: mscorlib.dll!System.Reflection.Assembly.GetTypes() + 0xcd bytes PaintDotNet.exe!PaintDotNet.FileTypes.GetFileTypeFactoriesFromAssembly(System.Reflection.Assembly assembly) Line 54 + 0xc bytes C# PaintDotNet.exe!PaintDotNet.FileTypes.GetFileTypeFactoriesFromAssemblies(System.Collections.ICollection assemblies) Line 75 + 0x5 bytes C# PaintDotNet.exe!PaintDotNet.FileTypes.LoadFileTypes() Line 149 + 0x9 bytes C# PaintDotNet.exe!PaintDotNet.Controls.DocumentWorkspace.ChooseFiles(System.Windows.Forms.Control owner, out string[] fileNames, bool multiselect, string startingDir) Line 1788 + 0x19 bytes C# PaintDotNet.exe!PaintDotNet.Actions.OpenFileAction.PerformAction(PaintDotNet.Controls.AppWorkspace appWorkspace) Line 41 C# PaintDotNet.exe!PaintDotNet.Controls.AppWorkspace.PerformAction(PaintDotNet.Actions.AppWorkspaceAction performMe) Line 1844 + 0xc bytes C# PaintDotNet.exe!PaintDotNet.Controls.AppWorkspace.CommonActionsStrip_ButtonClick(object sender, PaintDotNet.EventArgs<PaintDotNet.CommonAction> e) Line 2555 C# PaintDotNet.exe!PaintDotNet.Controls.CommonActionsStrip.OnButtonClick(PaintDotNet.CommonAction action) Line 121 + 0x26 bytes C# PaintDotNet.exe!PaintDotNet.Controls.CommonActionsStrip.OnItemClicked(System.Windows.Forms.ToolStripItemClickedEventArgs e) Line 133 C# System.Windows.Forms.dll!System.Windows.Forms.ToolStrip.HandleItemClick(System.Windows.Forms.ToolStripItem dismissingItem) + 0x39 bytes System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.HandleClick(System.EventArgs e) + 0x90 bytes System.Windows.Forms.dll!System.Windows.Forms.ToolStripItem.HandleMouseUp(System.Windows.Forms.MouseEventArgs e) + 0x1f5 bytes System.Windows.Forms.dll!System.Windows.Forms.ToolStrip.OnMouseUp(System.Windows.Forms.MouseEventArgs mea) + 0xfa bytes System.Windows.Forms.dll!System.Windows.Forms.Control.WmMouseUp(ref System.Windows.Forms.Message m, System.Windows.Forms.MouseButtons button, int clicks) + 0x3e7 bytes System.Windows.Forms.dll!System.Windows.Forms.Control.WndProc(ref System.Windows.Forms.Message m) + 0xd39 bytes System.Windows.Forms.dll!System.Windows.Forms.ToolStrip.WndProc(ref System.Windows.Forms.Message m) + 0x9d bytes PaintDotNet.SystemLayer.dll!PaintDotNet.SystemLayer.ToolStripEx.WndProc(ref System.Windows.Forms.Message m) Line 84 C# System.Windows.Forms.dll!System.Windows.Forms.Control.ControlNativeWindow.WndProc(ref System.Windows.Forms.Message m) + 0x46 bytes System.Windows.Forms.dll!System.Windows.Forms.NativeWindow.Callback(System.IntPtr hWnd, int msg = 514, System.IntPtr wparam, System.IntPtr lparam) + 0xb5 bytes [Native to Managed Transition] [Managed to Native Transition] System.Windows.Forms.dll!System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(int dwComponentID, int reason, int pvLoopData) + 0x5c3 bytes System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(int reason = -1, System.Windows.Forms.ApplicationContext context = {System.Windows.Forms.ApplicationContext}) + 0x578 bytes System.Windows.Forms.dll!System.Windows.Forms.Application.ThreadContext.RunMessageLoop(int reason, System.Windows.Forms.ApplicationContext context) + 0x65 bytes PaintDotNet.exe!PaintDotNet.Startup.StartPart2(string mutexName) Line 397 + 0x9 bytes C# PaintDotNet.exe!PaintDotNet.Startup.Main(string[] args) Line 428 + 0xb bytes C# And the loader exception was: Could not load type 'ImageFormatInfo' from assembly 'VtfFileType, Version=1.0.5.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 4 that is incorrectly aligned or overlapped by a non-object field. I discovered that it does not like the following struct: [structLayout(LayoutKind.Explicit)] public struct ImageFormatInfo { [FieldOffset(0)] [MarshalAs(UnmanagedType.LPStr)] public string sName; [FieldOffset(4)] public uint uiBitsPerPixel; [FieldOffset(8)] public uint uiBytesPerPixel; [FieldOffset(12)] public uint uiRedBitsPerPixel; [FieldOffset(16)] public uint uiGreenBitsPerPixel; [FieldOffset(20)] public uint uiBlueBitsPerPixel; [FieldOffset(24)] public uint uiAlphaBitsPerPixel; [FieldOffset(28)] [MarshalAs(UnmanagedType.U1)] public bool bIsCompressed; [FieldOffset(29)] [MarshalAs(UnmanagedType.U1)] public bool bIsSupported; } This makes sense because the pointer is 8 bytes in x64. However, it poses an interesting question: How do I explicitly layout a struct in such a way that it works in both x64 and x86 when it contains pointers if the assembly fails to load when it's wrong (even if it's never used - e.g. I can't just define it both ways)? Perhaps [structLayout(LayoutKind.Sequential, Pack=1)] will work; I'll try that tomorrow... Also I noticed Paint.Net uses Assembly.GetTypes(); is there some reason it doesn't use Assembly.GetExportedTypes()? I have used the later to eliminated problems loading certain assemblies in the past. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 25, 2010 Share Posted April 25, 2010 Unless you're implementing a structure that contains a union, you shouldn't have to specify LayoutKind.Explicit. You should only need to specify a special Pack value if the C/C++ struct also does. If you copy/paste the C/C++ definition for that struct, I can tell you if your interop definition is correct. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 25, 2010 Author Share Posted April 25, 2010 I first wrote the plug-in in .Net 2; the Pack attribute was added in .Net 3 so it wasn't an option. Interestingly, the following does not work: [structLayout(LayoutKind.Sequential, Pack=1, Size=34, CharSet=CharSet.Ansi)] //[structLayout(LayoutKind.Explicit)] public struct ImageFormatInfo { //[FieldOffset(0)] [MarshalAs(UnmanagedType.LPStr)] public string sName; //[FieldOffset(8)] public uint uiBitsPerPixel; //[FieldOffset(12)] public uint uiBytesPerPixel; //[FieldOffset(16)] public uint uiRedBitsPerPixel; //[FieldOffset(20)] public uint uiGreenBitsPerPixel; //[FieldOffset(24)] public uint uiBlueBitsPerPixel; //[FieldOffset(28)] public uint uiAlphaBitsPerPixel; //[FieldOffset(32)] [MarshalAs(UnmanagedType.U1)] public bool bIsCompressed; //[FieldOffset(33)] [MarshalAs(UnmanagedType.U1)] public bool bIsSupported; } [DllImport("VTFLib.dll", CallingConvention = CallingConvention.Cdecl)] [return:MarshalAs(UnmanagedType.U1)] public unsafe static extern bool vlImageGetImageFormatInfoEx(ImageFormat ImageFormat, out ImageFormatInfo ImageFormatInfo); VtfLib.ImageFormatInfo Info; if (VtfLib.vlImageGetImageFormatInfoEx(Token.eImageFormat, out Info)) // This line crashes. { } Using this C++ declaration: typedef char vlChar; typedef unsigned char vlBool; typedef unsigned int vlUInt; #pragma pack(1) typedef struct tagSVTFImageFormatInfo { vlChar *lpName; vlUInt uiBitsPerPixel; vlUInt uiBytesPerPixel; vlUInt uiRedBitsPerPixel; vlUInt uiGreenBitsPerPixel; vlUInt uiBlueBitsPerPixel; vlUInt uiAlphaBitsPerPixel; vlBool bIsCompressed; vlBool bIsSupported; } SVTFImageFormatInfo; #pragma pack() sizeof(SVTFImageFormatInfo) = 34 Go figure. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 26, 2010 Share Posted April 26, 2010 Ok you say "This line crashes", but please remember that I am not psychic. I need the exception. Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 26, 2010 Author Share Posted April 26, 2010 It doesn't give me one. Even with the debugger attached and visual studio set to break on all exceptions. All I get is a dialog that says that Paint.Net has stopped working. The debugger catches other exceptions just fine... Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 26, 2010 Share Posted April 26, 2010 You're probably stomping memory or stack then. What's the definition in C/C++ for that function you're calling? Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 26, 2010 Author Share Posted April 26, 2010 VTFLIB_API vlBool vlImageGetImageFormatInfoEx(VTFImageFormat ImageFormat, SVTFImageFormatInfo *VTFImageFormatInfo) { if(ImageFormat >= 0 && ImageFormat < IMAGE_FORMAT_COUNT) { memcpy(VTFImageFormatInfo, &CVTFFile::GetImageFormatInfo(ImageFormat), sizeof(SVTFImageFormatInfo)); return vlTrue; } return vlFalse; } I tried changing the out to a ref to no avail (and creating an instance of ImageFormatInfo beforehand). This is the only function that doesn't seem to be working. Interestingly this nearly identical function does work: [DllImport("VTFLib.dll", CallingConvention = CallingConvention.Cdecl)] public unsafe static extern void vlImageCreateDefaultCreateStructure(out CreateOptions CreateOptions); The only significant difference I can see is that the problem function's struct has a string in it. Quote Link to comment Share on other sites More sharing options...
Rick Brewster Posted April 26, 2010 Share Posted April 26, 2010 Change it from a string to an IntPtr, and remove the [MarshalAs] attribute. I assume VTFImageFormat is an enum, with implicit type of int? Quote The Paint.NET Blog: https://blog.getpaint.net/ Donations are always appreciated! https://www.getpaint.net/donate.html Link to comment Share on other sites More sharing options...
Nem Posted April 27, 2010 Author Share Posted April 27, 2010 Using an IntPtr does work; interesting how [MarshalAs(UnmanagedType.LPStr)] did work before though... Anyways, I can use System.Runtime.InteropServices.Marshal.PtrToStringAnsi(Info.sName) to get the string. Thanks for all your help Rick. Everything seems to be working now. Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.