C#: One attribute for multiple declarations (DLLImport) - c#

I'm using the [DLLImport] attribute to access a bunch of C++ functions in my .NET code.
For now, I have all the functions in the following way:
const string DLL_Path = "path\\to\\my\\dll.dll";
[DllImport(DLL_Path,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern int MyFunction1();
[DllImport(DLL_Path,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern ErrorCode MyFunction2(int id);
[DllImport(DLL_Path,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern ErrorCode MyFunction3(string server, byte timeout,
ref int connection_id, ref DeviceInfo pInfos);
[DllImport(DLL_Path,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern ErrorCode MyFunction4([MarshalAs(UnmanagedType.LPArray)] byte[] pVersion,
ref int psize);
[DllImport(DLL_Path,
CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern ErrorCode MyFunction5(int errorcode,
[MarshalAs(UnmanagedType.LPTStr)] string pmsg, ref int psize);
Which is rather not pleasing to the eye: the repetition of attributes seems unefficient and destroys readability for the prototypes of the functions. Especially since I have something like 20 or 30 functions to import.
I wonder if I could have the [DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] part only once somewhere and have the function definitions more clearly identified, like this pseudo code:
const string DLL_Path = "path\\to\\my\\dll.dll";
// some code defining a section which tells that the next functions are DLLImport
[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
{
public static extern int MyFunction1();
public static extern ErrorCode MyFunction2(int id);
public static extern ErrorCode MyFunction3(string server, byte timeout, ref int connection_id, ref DeviceInfo pInfos);
public static extern ErrorCode MyFunction4([MarshalAs(UnmanagedType.LPArray)] byte[] pVersion, ref int psize);
public static extern ErrorCode MyFunction5(int errorcode, [MarshalAs(UnmanagedType.LPTStr)] string pmsg, ref int psize);
}
Is this possible?
I found this question in SO: Shorten amount of DllImport in C#? but it suggests dynamically loading the functions through LoadLibrary and GetProcAddress, which I find less readable.

No, there's no way to reduce the Attributes to a single declaration. You'll need to apply the Attribute to all methods.
But you can at least shorten your Attribute declarations to [DllImport(DLL_Path)], because the values you are specifying for CallingConvention and CharSet are the same as the default values.

Related

Why FindNextFile is not working in my C# code?

The code is not working for me. I am using Visual Studio 2008 and .NET 3.5. I am not getting file name. The return value from FindNextFile is null. The reason not to use Directory.GetFiles() is that i want to traverse file one by one. For Directory.GetFiles(), i am gettign all the files in array at a time
struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
class Program
{
[DllImport("kernel32", CharSet = CharSet.Auto)]
public static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
public static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
public static extern bool FindClose(IntPtr hFindFile);
static void Main(string[] args)
{
StreamWriter sw = new StreamWriter("dir.txt", false);
string dir = Path.GetFullPath(System.IO.Directory.GetCurrentDirectory());
WIN32_FIND_DATA wfd = new WIN32_FIND_DATA();
IntPtr h = FindFirstFile((dir + #"\*.*"), out wfd);
while (FindNextFile(h, out wfd))
sw.WriteLine(wfd.cFileName);
FindClose(h);
sw.Flush();
sw.Close();
}
}
Many Windows APIs support two string types: ANSI (8-bit characters) and Wide (16-bit characters). The problem here seems to be that you are calling the wide version of the APIs but interpreting the results as ANSI.
You can fix this by marking the WIN32_FIND_DATA struct as unicode (or auto). CharSet.Auto and CharSet.Unicode seem to do the same thing here.
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Auto)]
struct WIN32_FIND_DATA {
UPDATE
Try adding an [Out] attribute to the imports as suggested by Hans Passant.
[DllImport("kernel32", CharSet = CharSet.Auto)]
public static extern IntPtr FindFirstFile(string lpFileName, [Out] out WIN32_FIND_DATA lpFindFileData);
[DllImport("kernel32", CharSet = CharSet.Auto)]
public static extern bool FindNextFile(IntPtr hFindFile, [Out] out WIN32_FIND_DATA lpFindFileData);

Automatically set security attributes when moving files with Win32 API ? (C#)

I'm currently developing an app that have to move a file to another application folder. But the other application must have a specific permissions on files that are copied.
When I use the Win32 API to MoveFileFromApp (it's uwp app), it doesn't update the security attributes to inherit the folder.
here is the code of MoveFile (C#)
[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern bool MoveFileFromApp(
string lpExistingFileName, string lpNewFileName);
Does anyone know how to set these security permissions for the folder automatically during the moving process ?
Thank you very much !
I tried to use SetNamedSecurityInfo method by P/Invoke in UWP app with the following code. The error ERROR_ACCESS_DENIED(error code is 5) always exists in UWP app. But in Win32, the usage of SetNamedSecurityInfo method with the value UNPROTECTED_DACL_SECURITY_INFORMATION to inherit the security attributes is successful.
In UWP, there are some limitations on file system access permission. Though the SetNamedSecurityInfo method can be called successfully, it does not work as expected.
The code to use SetNamedSecurityInfo method in UWP:
public uint DACL_SECURITY_INFORMATION = 0x00000004;
public uint UNPROTECTED_DACL_SECURITY_INFORMATION = 0x20000000;
public uint ACL_REVISION = 0x2;
[StructLayout(LayoutKind.Sequential)]
public struct ACL
{
public byte AclRevision;
public byte Sbz1;
public ushort AclSize;
public ushort AceCount;
public ushort Sbz2;
}
public enum SE_OBJECT_TYPE
{
SE_UNKNOWN_OBJECT_TYPE = 0,
SE_FILE_OBJECT,
SE_SERVICE,
SE_PRINTER,
SE_REGISTRY_KEY,
SE_LMSHARE,
SE_KERNEL_OBJECT,
SE_WINDOW_OBJECT,
SE_DS_OBJECT,
SE_DS_OBJECT_ALL,
SE_PROVIDER_DEFINED_OBJECT,
SE_WMIGUID_OBJECT,
SE_REGISTRY_WOW64_32KEY,
SE_REGISTRY_WOW64_64KEY,
}
[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool MoveFileFromAppW(
string lpExistingFileName,
string lpNewFileName);
[DllImport("advapi32.DLL", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern uint SetNamedSecurityInfo(
string lpFileName,
SE_OBJECT_TYPE ObjectType,
uint SecurityInfo,
IntPtr psidOwner,
IntPtr psidGroup,
ref ACL pDacl,
IntPtr pSacl);
[DllImport("advapi32.dll", SetLastError = true)]
internal static extern bool InitializeAcl(ref ACL pAcl, int nAclLength, uint dwAclRevision);
[DllImport("api-ms-win-core-file-fromapp-l1-1-0.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall,
SetLastError = true)]
internal static extern bool MoveFileFromApp(
string lpExistingFileName,
string lpNewFileName
);
public void SetFilePermission(string FileName)
{//Use the method to let the file inherit the security attributes from parent object
bool ret = false;
ACL pDacl = new ACL();
ret = InitializeAcl(ref pDacl, Marshal.SizeOf<ACL>(), ACL_REVISION);
uint SecurityInfo = DACL_SECURITY_INFORMATION | UNPROTECTED_DACL_SECURITY_INFORMATION;
uint err = SetNamedSecurityInfo(FileName, SE_OBJECT_TYPE.SE_FILE_OBJECT, SecurityInfo, IntPtr.Zero, IntPtr.Zero, ref pDacl, IntPtr.Zero);
}

UXTheme dll symbols and AccentColor

I was trying to get Accent Color for use in my WPF app, and i found one class in GitHub that always is the same.
static class UxTheme
{
[DllImport("uxtheme.dll", EntryPoint = "#98", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern uint GetImmersiveUserColorSetPreference(bool forceCheckRegistry, bool skipCheckOnFail);
[DllImport("uxtheme.dll", EntryPoint = "#94", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern uint GetImmersiveColorSetCount();
[DllImport("uxtheme.dll", EntryPoint = "#95", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern uint GetImmersiveColorFromColorSetEx(uint immersiveColorSet, uint immersiveColorType,
bool ignoreHighContrast, uint highContrastCacheMode);
[DllImport("uxtheme.dll", EntryPoint = "#96", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern uint GetImmersiveColorTypeFromName(IntPtr name);
[DllImport("uxtheme.dll", EntryPoint = "#100", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
public static extern IntPtr GetImmersiveColorNamedTypeByIndex(uint index);
}
I was finding uxtheme documentation in SDK and MSDN but this methods aren't.
I dumpbin uxtheme.dll and surprise! they are unnamed functions. (Except #95 & #98).
How can i get signatures of the uxtheme (or others) library? Then, i can try-error guessing the functions i need.

FindNextFile ERROR_INVALID_NAME

I'm trying to use the WINAPI functions FindFirstFile and FindNextFile.
However, I'm experiencing some issues.
When I first call the function FindFirstFile it works fine. I've got a valid handler and the first folder/file name is properly populated inside the WIN32_FIND_DATA structure. No error is found with GetLastError.
Then, I call FindNextFile which returns true as there are more folders in the directory I'm scanning. But I can't retrieve the next folder/file name and GetLastError returns 123 (0x7B) ERROR_INVALID_NAME.
I'm a little confused as it says in the official documentation that if an error occurs it should return 0.
https://msdn.microsoft.com/en-us/library/windows/desktop/aa364428(v=vs.85).aspx
Return value
If the function succeeds, the return value is nonzero and the lpFindFileData parameter contains information about the next file or directory found.
If the function fails, the return value is zero and the contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function.
If the function fails because no more matching files can be found, the GetLastError function returns ERROR_NO_MORE_FILES.
I'm using .NET 4.5.1 and Visual Studio 2013 on Windows 7 x64.
Here is a sample code.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct WIN32_FIND_DATA
{
public uint dwFileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
public uint nFileSizeHigh;
public uint nFileSizeLow;
public uint dwReserved0;
public uint dwReserved1;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
public string cFileName;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
public string cAlternateFileName;
}
...
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile, ref WIN32_FIND_DATA lpFindFileData);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindClose", SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
...
public static void Test()
{
WIN32_FIND_DATA metaDataFile = new WIN32_FIND_DATA();
IntPtr nextHandle = FileScanner.FindFirstFile("C:\\*", ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x0 ERROR_SUCCESS
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin
/* Check invalid handler */
if (nextHandle != new IntPtr(-1L))
{
bool moreFiles = true;
while (moreFiles)
{
moreFiles = FileScanner.FindNextFile(nextHandle, ref metaDataFile);
Console.WriteLine(Marshal.GetLastWin32Error()); // This equals 0x7B ERROR_INVALID_NAME
Console.WriteLine(metaDataFile.cFileName); // This equals $Recycle.Bin and this value never change.
}
FindClose(nextHandle);
}
}
For some reason, moreFiles is always true and GetLastError returns ERROR_INVALID_NAME ...
If you need any details please ask me.
Any help would really be appreciated !
Only call Marshal.GetLastWin32Error is the API call reports failure. In the case of FindNextFile it does so by returning false. You are checking the value returned by Marshal.GetLastWin32Error indiscriminately.
The documentation makes this clear when it tells you how the functions indicate failure. You even linked the text. But you said:
I'm a little confused as it says in the official documentation that if an error occurs it should return 0.
That's right. So check the return value against 0. In the case of a BOOL marshalled as C# bool, that means that the function returns false if it fails. But you simply ignored the return value and tested the value returned by Marshal.GetLastWin32Error(), something altogether different.
The code should be more like this:
public static void Test()
{
WIN32_FIND_DATA fd = new WIN32_FIND_DATA();
IntPtr findHandle = FileScanner.FindFirstFile("C:\\*", ref fd);
if (findHandle == INVALID_HANDLE_VALUE)
throw new Win32Exception();
do
{
Console.WriteLine(fd.cFileName);
} while (FileScanner.FindNextFile(findHandle, ref fd));
// you might check that Marshal.GetLastWin32Error() returns ERROR_NO_MORE_FILES
// at this point, otherwise the enumeration failed abnormally
if (!FindClose(findHandle))
throw new Win32Exception();
}
Your other problem, and it's what hurting you most, is your p/invoke declarations. Look closely at this one:
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("Kernel32.dll", EntryPoint = "FindFirstFile", SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
The EntryPoint is not correct. So you are actually calling FindFirstFile instead of FindNextFile and it's hardly surprising that fails.
Specifying the EntryPoint when you don't need to is simply asking for trouble. And you've fallen right into the trap. I'd declare these imports like so:
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern IntPtr FindFirstFile(string lpFileName,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindNextFile(IntPtr hFindFile,
ref WIN32_FIND_DATA lpFindFileData);
[DllImport("Kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern bool FindClose(IntPtr hFindFile);
Note that there's no need for the return attribute since UnmanagedType.Bool is the default.
And then you'd need to change the CharSet on the struct to be CharSet.Unicode to match. There's no point at all in opting for ANSI here.
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct WIN32_FIND_DATA
{
....
}
Finally, all of this code seems to me to be pointless. What's wrong with Directory.EnumerateFiles and Directory.EnumerateDirectories?

Error using c DLL. Attempted to read or write protected memory

I try use a dll created in c in c#, but I get the error "Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
I see very topics here, but i'm not solve the problem. It's possible help me?
My C# code is:
[DllImport("kernel32", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern IntPtr LoadLibrary(string dllToLoad);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, string procedureName);
[DllImport("kernel32")]
public static extern bool FreeLibrary(IntPtr hModule);
[DllImport("lib.dll", EntryPoint = "LoadRes", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern SRes LoadRes([MarshalAs(UnmanagedType.LPStr)]string str, bool type);
SRes sres = new SRes();
string path="C:\\123456.fil";
System.IntPtr load = LoadLibrary("lib.dll");
System.IntPtr adress = GetProcAddress(load, "LoadRes");
sres = LoadRes(path, true);
My code to export in c is
__declspec(dllexport) SRes LoadRes( const char *filename, bool header_only );
Thank's for help!!

Categories

Resources