Jump to content

FileType Not Showing Up In Paint.Net


Nem

Recommended Posts

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!

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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);
}

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

does the reflectiontypeloadexception provide any more details?

xZYt6wl.png

ambigram signature by Kemaru

[i write plugins and stuff]

If you like a post, upvote it!

Link to comment
Share on other sites

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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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.

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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...

Link to comment
Share on other sites

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.

Link to comment
Share on other sites

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. :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...