I'm trying to use ShSetFolderPath function in C#. I work on Win7, I've managed to use ShSetKnownFolderPath and it works fine.
Since this function is unavaible in WinXP, i tried to invoke ShSetFolderPath. Because i'm not familiar with invoking, I've done some searching and found something on some French forum. I don't speak French, but this declaration makes sense (as written in Remarks of function documentation in MSDN library):
[DllImport( "Shell32.dll", CharSet = CharSet.Unicode, EntryPoint = "#232" ) ]
private static extern int SHSetFolderPath( int csidl, IntPtr hToken, uint flags, string path );
I call it like that:
private static int CSIDL_DESKTOP = 0x0000;
public static void SetDesktopPath(string path)
{
int ret;
ret = SHSetFolderPath(CSIDL_DESKTOP, IntPtr.Zero, 0, path);
if (ret != 0)
{
Console.WriteLine(ret);
Console.WriteLine(Marshal.GetExceptionForHR(ret));
}
}
It works in Win7, but in XP function returns -2147024809, which means "Value does not fall within the expected range".
My guess is, it's something wrong with Dll importing. Any idea?
Funny thing.
I've taken another look at CSIDL list. And I've realized I was trying to change some "low-level" reference (i guess) to desktop:
CSIDL_DESKTOP = 0x0000, // <desktop>
While I actually wanted to change just folder location, and i should've use this:
CSIDL_DESKTOPDIRECTORY = 0x0010, // <user name>\Desktop.
And THIS works.
It explains everything. Shame on me.
Nah, that's not it. The error code, converted to hex, is 0x80070057. The 7 indicates a Windows error, 57 is error code 87, ERROR_INVALID_PARAMETER, "The parameter is incorrect".
A couple of possible reasons. First is that entry point #232 isn't actually the entry point for SHSetFolderPath(). You might be calling a different function, it wouldn't know what to do with the argument values you pass. Hard to say, it is an unnamed entry point on XP's version of shell32.dll. Or it could be that XP just isn't happy about you changing the desktop folder path. Not that surprising, there's a heckofalot it has to do to actually implement that, refreshing all Explorer.exe views, rebuilding the desktop contents and whatnot.
Check this thread for possible help.
Related
I'm trying to wrap a C++ dll for which I only have the header file for. The function I am trying to get working at the moment is giving me an AccessViolationException:
"Attempted to read or write protected memory.
This is often an indication that other memory is corrupt."
The C++ function prototype is:
RunSimulation( LPCSTR, LPSTR);
Meanwhile, my C# wrapper is:
[DllImport("thedll.dll", EntryPoint = "RunSimulation")]
public static extern uint RunSimulation(string simID, ref string outputID);
I suspect the problem is with the C# function, specifically with how the strings are implemented. Since I'm relatively new to platform invocation and such, I'm not sure how to proceed.
Should the string parameters be pointers to where the strings are? Or is there something else wrong with the wrapper maybe?
Edit:
This is how the function is called on the managed end of things:
string outputID = "";
try
{
RunSimulation(id, ref outputID);
}
catch (Exception e)
{
Logging.Log_Warning.SendException("Threw an exception", e);
}
Edit 2:
Upon changing the second parameter to a StringBuilder, the same exception occurs. The only difference is that the exception break doesn't stop at the line where the function is called, Visual Studio opens a new "Break" tab saying the exception happened. The function documentation recommends setting aside 16 bytes or more, so I used the capacity constructor with a values of 1024 and 4096 to test.
Edit 3:
After doing a clean and rebuild, the problem presented itself as a driver error. Since this shows that the API is functioning, the solution was indeed to change my ref string parameter to a StringBuilder as suggested in the comments.
The solution to my problem ended up being to use StringBuilder instead of String to make sure the space in memory was allocated ahead of time. So my signature ended up looking like:
[DllImport("thedll.dll", EntryPoint = "RunSimulation")]
public static extern uint RunSimulation(string simID, StringBuilder outputID);
And to use it:
string id = "someID";
int requiredSize = 512;
StringBuilder outputID = new StringBuilder(requiredSize);
RunSimulation(id, outputID);
Hope that helps!
I'm rewording this question since I understand a bit more now. Originally, what I had was too vague. I've discovered that I'm being routed by something called "Code Access Security." This is old-hat to everyone reading this, I'm sure, but not to me.
The application is very large so in a nutshell I have two assemblies. One is a utilities assembly with various "tools" used throughout the program. The other is calling upon these tools in order to function.
In the utilities assembly, there are many functions that are PInvoked but the one giving me grief is: SetupDiGetDeviceInterfaceDetail() (see here). My function prototype looks like this:
[DllImport("SetupApi.dll", SetLastError = true, CharSet = CharSet.Auto)]
[return : MarshalAs(UnmanagedType.Bool)]
public static extern bool SetupDiGetDeviceInterfaceDetail(
SafeHandleZeroOrMinusOneIsInvalid deviceInfoSet,
ref SP_DEVICE_INTERFACE_DATA deviceInterfaceData,
IntPtr deviceInterfaceDetailData,
uint deviceInterfaceDetailDataSize,
IntPtr requiredSize,
IntPtr deviceInfoData);
In the assembly which uses this function, I'm using the two step process outlined in the remarks in order to gain the understanding of how much space I need to store the DevicePath which is in the SP_DEVICE_INTERFACE_DETAIL_DATA structure (see here). For example:
string GetDevicePath(SafeHandleSeroOrMinusOneIsInvalid hList, SP_DEVICE_INTERFACE_DATA infoSet)
{
IntPtr pReqSize = Marshal.AllocHGlobal(4);
Marshal.WriteInt32(pReqSize, 0);
uint reqSize;
// get the size needed
PInvoke.SetupDiGetDeviceInterfaceDetail(hList,
ref infoSet,
IntPtr.Zero,
0,
pReqSize,
IntPtr.Zero);
reqSize = (uint)Marshal.ReadInt32(pReqSize, 0);
IntPtr pDevInfoDetail = Marshal.AllocHGlobal((int)reqSize + 4); // +4 for cbSize
// call again, this time getting the actual data wanted
PInvoke.SetupDiGetDeviceInterfaceDetail(hList,
ref infoSet,
pDevInfoDetail,
reqSize,
IntPtr.Zero,
IntPtr.Zero);
string path;
// work .NET magic to read from unmanaged memory the path string and assign it
// to the above variable. Deallocate both unmanaged memory blocks.
return path;
}
The most frustrating thing is, these assemblies are used by two different programs. One is a GUI using the Visual Studio Isolated Shell. The other is simply a command line program. When the GUI is running, the above code is called and executes as expected. In the command line tool however, they fail (as described in the MSDN reference for this Setup API function) with some data about what happened. At this point, I'm able only to recover a portion of the data that is returned.
This is what comes back from the runtime:
stem.Security.PartialTrustVisibilityLevel, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
I know this has something to do with Code Access Security but I'm not at all sure how to fix. Using some suggestions that I've found thus far I've tried this attribute to the assembly (I placed it before the namespace block of code):
[assembly: AllowPartiallyTrustedCallers]
But this caused other compilation problems.
Please, anything would be most helpful and greatly appreciated.
Andy
Unfortunately, the problem isn't yet fixed. However, the problem appears to have nothing to do with Code Access Security as I first thought. I was being thrown a red herring. I stepped my way through the code using the memory window in Visual Studio and noticed that these strings were in memory before calling the Setup API function to fill them. Occasionally, I would get a different block of memory with different contents too, I just usually ended up with the contents I pasted.
The problem actually appears to have something to do with the 64 vs. 32 bit environments (at least, that's my theory at this point).
However, this question isn't the actual problem so I'm "answering" it to close it.
Hey guys and girls :) ok so i ran this project ->
http://www.helyar.net/2009/libvlc-media-player-in-c-part-2/ and it worked perfectly (he was using .net 2.0) however when i try anything above 3.5 it gives ->
Unable to load DLL ‘libvlc’: The specified module could not be found. (Exception from HRESULT: 0x8007007E)
is there any workaround someone has done that sorts this out? MANY thanks ppl :D:D:D:D
There are two things that must be done when using that example with the new 2.0.x VLC releases. First, you have to somehow add the libvlc DLL to the search path. I used a call to SetDllDirectory to do the trick. You declare it as:
static class LibVlc
{
. . .
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool SetDllDirectory(string lpPathName);
. . .
}
Then you can call this method with the root folder of the VLC installation. On my PC, I called it as follows:
LibVlc.SetDllDirectory(#"C:\Program Files (x86)\VideoLAN\VLC");
Obviously, for a program being distributed this parameter should be configurable.
Next, the VLC API's have apparently changed because none of the methods require an exception object to be passed in anymore. It looks like return values from the methods should be checked (for example, libvlc_new() returns NULL if there was an error). I haven't tried passing in the exception object by reference like he does but the calls all work fine without it (and my interfaces now match the VLC API exactly). I also specify the calling convention to use when doing interop, just to be clear to the runtime what I expect for parameter passing order and such. For example, here are my defines for libvlc_new and libvlc_release:
[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
public static extern IntPtr libvlc_new(int argc,
[MarshalAs(UnmanagedType.LPArray,
ArraySubType = UnmanagedType.LPStr)] string[] argv);
[DllImport("libvlc", CallingConvention=CallingConvention.Cdecl)]
public static extern void libvlc_release(IntPtr instance);
I hope this helps!
You must copy libvlc.dll to your bin/debug folder. It must be the one from your VLC installation folder (C:\program files\videolan\vlc)
I'm trying to pinvoke to a clutter function.
The function is defined in the docs as
ClutterActor * clutter_texture_new_from_file (const gchar *filename, GError **error);
The code I have is as follows:
[DllImport ("libclutter-glx-1.0.so.0")]
private static extern IntPtr clutter_texture_new_from_file (string filename, IntPtr errorData);
And I call it like this:
IntPtr texture = clutter_texture_new_from_file("myImage.jpeg",IntPtr.Zero);
however when called like this in monodevelop on ubuntu I get the following error.
Unix Transport Error
Eventally I would like to get the error reporting working so I can get the gerror result however firstly I need to get past the Unix Transport Error.
The errorData parameter should be marked as "ref IntPtr", although I don't think that should be causing this error since that parameter should be allowed to be NULL. Otherwise, try running this outside Monodevelop. This kind of error may be the result of a segfault elsewhere in your program.
Running a c# console app I wrote on 64 bit Vista. Here's the code:
class Class1
{
static void Main(string[] args)
{
Debug.Assert(File.Exists(#"c:\test.ini"));
StringBuilder sb = new StringBuilder(500);
uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, sb.Capacity, #"c:\test.ini");
Console.WriteLine(sb.ToString());
}
[DllImport("kernel32.dll")]
static extern uint GetPrivateProfileString(string lpAppName, string lpKeyName, string lpDefault, StringBuilder lpReturnedString, int nSize, string lpFileName);
}
I'm sure I'll get a big "DUH!" for an answer, but I'm not seeing why this fails to work. Other than the Debug.Assert, this code was cut from the c# sample at this page
This one has been busting my chops all day, too. I think I found a work-around, which I really don't want to have to deal with: insert a blank line before the first section header in your .ini file. Now run your app and see if you don't start seeing the values you were expecting.
Considering this bug has apparently been around for years, I'm surprised MS hasn't fixed it by now. But then, .ini files were supposed to have gone away years ago. Which of course is funny, because there are a lot of places MS uses .ini file (e.g, desktop.ini). But I think MS is aware of the bug, because I notice my desktop.ini files include a leading blank line. Hmmm...
The main thing I see is that you should be passing in an uint for nSize, as well as the return value. This is because the return and nSize parameters of GetPrivateProfileString are DWORD values, which are unsigned 32bit integers.
I personally have used the syntax on PInvoke.net:
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
In addition, you'll need to put the full path to the file in place, unless the file is located in the Windows directory. From the docs:
If this parameter does not contain a full path to the file, the system searches for the file in the Windows directory.
According to pinvoke.net, nSize should be a UINT. Also they are using an absolute path in their example.
Other than those differences, I can't see anything else.
Providing it's throwing a invalid format exception, try setting target platform to x86 to solve the problem.
Usage example from pinvoke.net is
[DllImport("kernel32.dll", CharSet=CharSet.Unicode)]
static extern uint GetPrivateProfileString(
string lpAppName,
string lpKeyName,
string lpDefault,
StringBuilder lpReturnedString,
uint nSize,
string lpFileName);
static void Main(string[] args)
{
StringBuilder sb = new StringBuilder(500);
uint res = GetPrivateProfileString("AppName", "KeyName", "", sb, (uint)sb.Capacity, #"c:\test.ini");
Console.WriteLine(sb.ToString());
}
If no path is specified, GetPrivateProfileString will look for Test.ini in the Windows directory.
Old APIs like GetPrivateProfileString don't handle Unicode well (even though there's a GetPrivateProfileStringW function). If Test.ini contains a UTF header or Unicode characters, that might be enough to prevent GetPrivateProfileString from working correctly.
Also, Vista's UAC can make handling files that are in "special" places tricky (C:\, C:\Windows, C:\Program Files, etc.). Try putting Test.ini in a folder rather than in the root of the C: drive, or turn off UAC. There's a thread on CodeProject that discusses GetPrivateProfileString failing silently when trying to read an .ini from a folder controlled by UAC.
Maybe you should look into looking at an open source solution that will do exactly that without you worrying about p/Invoke's. The project targetted for .NET is called nini I am using this in a project I am working on and I recommend it.
Hope this helps,
Best regards,
Tom.
Can you verify the contents of your test.ini file? Given all of the steps that you've tried, I'm beginning to suspect that your data file is not formatted correctly (misspelling, etc.) In other words, GetPrivateProfileString may be "working", but just not finding your string. Based on the code that you posted, your test.ini file should look something like this:
[AppName]
KeyName=foo