Updating images in resource section of an exe (in c#/C) - c#

I have few images embedded in my executable in resource section.
I followed these steps to create my executable:
Generated .resx file for all the images (.jpg) in a directory using some utility. The images are named image1.jpg, image2.jpg and so on.
created .resources file from .resx file using: resgen myResource.resx
Embedded the generated .resource file using /res flag as: csc file.cs /res:myResource.resources
4 I am accessing these images as:
ResourceManager resources = new ResourceManager("myResource", Assembly.GetExecutingAssembly());
Image foo = (System.Drawing.Image)(resources.GetObject("image1"));
This all is working fine as expected. Now I want to change embedded images to some new images. This is what I am currently doing:
class foo
{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, string wLanguage, Byte[] lpData, uint cbData);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
public static void Main(string[] args)
{
IntPtr handle = BeginUpdateResource(args[0], false);
if (handle.ToInt32() == 0)
throw new Exception("File Not Found: " + fileName + " last err: " + Marshal.GetLastWin32Error());
byte[] imgData = File.ReadAllBytes("SetupImage1.jpg");
int fileSize = imgData.Length;
Console.WriteLine("Updaing resources");
if (UpdateResource(handle, "Image", "image1", "image1", imgData, (uint)fileSize))
{
EndUpdateResource(handle, false);
Console.WriteLine("Update successfully");
}
else
{
Console.WriteLine("Failed to update resource. err: {0}", Marshal.GetLastWin32Error());
}
}
}
The above code is adding a new resource for the specified image (inside IMAGE title with some random number, as seen in Resource hacker), but I want to modify the existing resource data for image1.
I am sure that I am calling UpdateResource with some invalid arguments.
Could any one help pointing that out?
I am using .NET version 2
Thank you,
Vikram

I think you are making a confusion between .NET resources, and Win32 resources. The resources you add embedding with the /res argument to csc.exe are .NET resources that you can successfully read using you ResourceManager snippet code.
Win32 resources are another beast, that is not much "compatible" with the managed .NET world in general, athough you can indeed add them to a .NET exe using the /win32Res argument - note the subtle difference :-)
Now, if you want to modify embedded .NET resources, I don't think there are classes to do it in the framework itself, however you can use the Mono.Cecil library instead. There is an example that demonstrates this here: C# – How to edit resource of an assembly?
And if you want to modify embedded Win32 resources, you code needs some fixes, here is a slightly modified version of it, the most important difference lies in the declaration of UpdateResource:
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr BeginUpdateResource(string pFileName, bool bDeleteExistingResources);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool UpdateResource(IntPtr hUpdate, string lpType, string lpName, short wLanguage, byte[] lpData, int cbData);
[DllImport("kernel32.dll")]
static extern bool EndUpdateResource(IntPtr hUpdate, bool fDiscard);
public static void Main(string[] args)
{
IntPtr handle = BeginUpdateResource(args[0], false);
if (handle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error()); // this will automatically throw an error with the appropriate human readable message
try
{
byte[] imgData = File.ReadAllBytes("SetupImage1.jpg");
if (!UpdateResource(handle, "Image", "image1", (short)CultureInfo.CurrentUICulture.LCID, imgData, imgData.Length))
throw new Win32Exception(Marshal.GetLastWin32Error());
}
finally
{
EndUpdateResource(handle, false);
}
}

This is impossible. You cant modify compiled file that you are running.

I believe you can add new images in run time but can't update a resource that is essentially just held in memory.
If you add a resource in run time, it exists but I don't think it is compiled and therefore I don't think it is accessible to you.
Is there a reason you aren't using content instead?

Related

LoadLibraryEx Function Probelem In C#

I try to use LoadLibraryEx Function to load a xxx.dll. In ASP.NET CORE Web - MVC application. It can return a right value. But in a ASP.NET Web - Web API application. It return 0x00000000.But GetLastError() return 0. Here is my demo code
CODE IN ASP.NET Web - Web API application
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
private static uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
private static void LoadWin32Library(string libPath)
{
IntPtr moduleHandle = LoadLibraryEx(libPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
uint code = GetLastError();
Console.WriteLine(code);
Console.WriteLine(moduleHandle);
if (moduleHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
// GET api/values
public IEnumerable<string> Get()
{
String lpFileName = "C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\2.1.11\\System.dll";
LoadWin32Library(lpFileName);
return new string[] { "value1", "value2" };
}
CODE IN ASP.NET CORE Web - MVC
[DllImport("kernel32.dll")]
static extern uint GetLastError();
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryEx", SetLastError = true)]
private static extern IntPtr LoadLibraryEx(string lpFileName, IntPtr hFile, uint dwFlags);
private static uint LOAD_WITH_ALTERED_SEARCH_PATH = 0x00000008;
private static void LoadWin32Library(string libPath)
{
IntPtr moduleHandle = LoadLibraryEx(libPath, IntPtr.Zero, LOAD_WITH_ALTERED_SEARCH_PATH);
uint code = GetLastError();
Console.WriteLine(code);
Console.WriteLine(moduleHandle);
if (moduleHandle == IntPtr.Zero)
throw new Win32Exception(Marshal.GetLastWin32Error());
}
public string Index()
{
String lpFileName = "C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\2.1.11\\System.dll";
LoadWin32Library(lpFileName);
return "This is my default action...";
}
In fact. These two pieces of code are basically the same.But the results are completely different.So. If anybody can help me?
This isn't really an "answer" but it's too involved to be a comment.
You should never need to call LoadLibrary from managed code
There are three ways DLLs can get loaded into managed code:
Your program has a referenced to a managed assembly (in the References section under the project in Solution Explorer)
You call Assembly.Load (or one of the variants) from your code, loading in a managed assembly
You use P/Invoke to load an unmanaged DLL and execute a function within it (like how you are loading kernel32.dll and calling GetLastError or LoadLibraryEx in your code)
And, for completeness (and on the advice of #SeanSkelly), adding a fourth option that does involve LoadLibary(Ex), but with severe restrictions. This is definitely not a recommended option.
Use LoadLibary(Ex), but with the caveat that you have to do much of the work of (and mimic the actions of) the P/Invoke marshaler. You also can't use this with a managed DLL because GetProcAddress will always fail on a managed assembly.
I have a guess as to why you are getting success in one case and a failure in the other. In one case, you are loading in the same System.dll that the framework has already loaded, so it just returns a handle to the loaded DLL. In the other, you are loading a different version of System.dll. The unmanaged loader doesn't understand versioning and strong names the way Assembly.Load does, so it says "hey, I already have a System.dll and it doesn't match up, so I'm failing your request". But, this is just a guess.
But, it doesn't really matter, you don't need to do this.

I have an error "Dll load error" while using Hybridizer in C#

I am using Hybridizer for the first time. I am using Windows 10, Visual Studio 2019, CUDA 10.1 and Hybridizer 1.3.0 "Released Sep. 5th 2019". Although I have followed their steps, I keep getting the same error:
Dll load error when loading Hello_World_CUDA.dll: 126
whenever I try to make any simple code to test it like this:
using System;
using Hybridizer.Runtime.CUDAImports;
public class Hello_World
{
[EntryPoint]
public static void Hello()
{
Console.Out.Write("Hello from GPU");
}
static void Main()
{
cuda.DeviceSynchronize();
HybRunner runner = HybRunner.Cuda().SetDistrib(1, 2);
runner.Wrap(new Hello_World()).Hello();
}
}
even when using their examples without any change.
How can I solve this problem?
To troubleshoot please follow the three following steps:
Check file exists and can be loaded from current directory
Make sure dll can be loaded using LoadLibrary
Verify symbol can be found using GetProcAddress
To facilitate your investigation, please find below code to run these assertions
using System;
using System.IO;
using System.Runtime.InteropServices;
using Hybridizer.Runtime.CUDAImports;
public class Hello_World
{
[EntryPoint]
public static void Hello()
{
Console.Out.Write("Hello from GPU");
}
[DllImport("kernel32.dll", EntryPoint = "LoadLibraryA", SetLastError = true)]
static extern IntPtr LoadLibrary([MarshalAs(UnmanagedType.LPStr)] string name);
[DllImport("kernel32.dll", EntryPoint = "FormatMessage", SetLastError = true, CharSet = CharSet.Unicode)]
static extern int FormatMessage(int dwFlags, IntPtr lpSource, int dwMessageId, int dwLanguageId, [MarshalAs(UnmanagedType.LPArray)] char[] data, uint dwSize, IntPtr args);
[DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true, CharSet = CharSet.Ansi)]
static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string symbol);
static unsafe string ErrorToString(int er)
{
char[] buffer = new char[2048];
int res = FormatMessage(0x00001000, // FORMAT_MESSAGE_FROM_SYSTEM
IntPtr.Zero, er,
0x0409, // US language -- in case of issue, replace with 0
buffer, 2048, IntPtr.Zero);
if (res == 0)
throw new ApplicationException(string.Format("Cannot format message - Error : {0}", res));
string resstring;
fixed (char* ptr = &buffer[0])
{
resstring = new string(ptr);
}
return resstring;
}
static void Main()
{
// Trouble-shooting
// print execution directory
Console.Out.WriteLine("Current directory : {0}", Environment.CurrentDirectory);
Console.Out.WriteLine("Size of IntPtr = {0}", Marshal.SizeOf(IntPtr.Zero));
// first, make sure file exists
string path = #"Troubleshooting_CUDA.dll"; // replace with actual dll name - you can read that on the output of the build
if (!File.Exists(path))
{
Console.Out.WriteLine("Dll could not be found in path, please verify dll is located in the appropriate directory that LoadLibrary may find it");
Environment.Exit(1);
}
// make sure it can be loaded -- open DLL in depends to missing troubleshoot dependencies (may be long to load)
IntPtr lib = LoadLibrary(path);
if (lib == IntPtr.Zero)
{
int code = Marshal.GetLastWin32Error();
string er = ErrorToString(code);
Console.Out.WriteLine("Dll could not be loaded : {0}", er);
Environment.Exit(2);
}
// finally try to get the proc address -- open DLL in depends to see list of symbols (may be long to load)
IntPtr procAddress = GetProcAddress(lib, "Hello_Worldx46Hello_ExternCWrapper_CUDA");
if (procAddress == IntPtr.Zero)
{
int code = Marshal.GetLastWin32Error();
string er = ErrorToString(code);
Console.Out.WriteLine("Could not find symbol in dll : {0}", er);
Environment.Exit(3);
}
cuda.DeviceSynchronize();
HybRunner runner = HybRunner.Cuda().SetDistrib(1, 2);
runner.Wrap(new Hello_World()).Hello();
}
}
If it fails at first step, you may need to change output directory of the CUDA satellite project, and/or execution directory of your application.
Should it fail at second step, you want to verify you execute in x64 (size of IntPtr should be 8), and that dll loads. Dependency walker, you may find here, is a great tool for this purpose, you want the x64 version. NOTE: loading may be very long.
Should it fail at third step, look-up the symbol name with depends, maybe your CUDA satellite project is not up to date.

How To Add Resources Without Compatibility Error?

The code below creates a copy of the application and adds resources to the copy. When you run the copy that has resources in it, it does it's job first. But when it exits, it exits with Program Compatibility Assistant error:
Image is from Google.
class Program
{
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern IntPtr BeginUpdateResource([MarshalAs(UnmanagedType.LPStr)] string filename, bool deleteExistingResources);
[DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
public static extern bool UpdateResource(IntPtr resource, [MarshalAs(UnmanagedType.LPStr)] string type, [MarshalAs(UnmanagedType.LPStr)] string name, ushort language, IntPtr data, uint dataSize);
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool EndUpdateResource(IntPtr resource, bool discardChanges);
private static void modifyResources(string filename)
{
IntPtr handle = BeginUpdateResource(filename, true);
UpdateResource(handle, "10", "1", 0, Marshal.StringToHGlobalAnsi("hello world"), (uint) 11);
EndUpdateResource(handle, false);
}
static void Main(string[] args)
{
string exeFilename = Process.GetCurrentProcess().MainModule.FileName;
string filename = Path.GetFileName(exeFilename);
string anotherFilename = exeFilename.Replace(filename, "_" + filename);
File.Copy(exeFilename, anotherFilename, true);
modifyResources(anotherFilename);
}
}
I don't get it. What mistakes do I make ?
More infos: Win 7 64x, App 86x
Notes (some of these made me think the error was gone):
maybe cleaning up imported libraries might help
maybe Assemblyname or namespace
seems the Compatibility Assistant checks too much and thinks something is wrong when the program does something different than the assistant expects.
project as single exe, no extra dlls (since extinguishing my extra dll, no error occurred)
definitely: error is not running-code related (empty main method)
definitely: error is filename related
Problem is the acquisitiveness of people! There are file names that are unwanted on Windows. Microsoft seems to prevent people from writing new installers. They spawn compatibility errors to prevent certain software from becoming popular and spreading.
Example:
If you use Resource Hacker (tested on Win7):
Go to the installation directory of Resource Hacker.
Run it, close it. No problem,
Rename ResHacker.exe to ResH Installer acker.exe
Run it, close it, see the problem.
Rename it to ResH 4kj545ui45kj4 acker.exe
Run it, close it. No problem.
Rename it back to ResHacker.exe
Punchline:
if (exeFilename.Contains("Installer") && exeFile.isCapableOfResourceManipulation())
makeProblem();
else
ignore();
// assembly info is checked too
// confirmed: after removing all unwanted keywords the error stays away
// even in my old project

C# Using VB6-Dll - AccessViolationException

I try to use a VB6 DLL in a C# Program. But I allways get a AccessViolationException. Maybe you can tell me what Im doing wrong.
I created a Test-VB6-DLL like in this tutorial:
http://oreilly.com/pub/a/windows/2005/04/26/create_dll.html
Then I tried to use this DLL dynamically like in this Post:
http://blogs.msdn.com/b/jonathanswift/archive/2006/10/03/dynamically-calling-an-unmanaged-dll-from-.net-_2800_c_23002900_.aspx?PageIndex=3#comments
But also if I try it by using [DLLImport]. I allways run into the AccessViolationException.
Maybe someone can give me a hint.
regards
viktor
P.S.: What I was able to do is to create a reference to an existing DLL. But this approach has the disadvantage, that I have to update all the references if the DLL is updated. And this will happen (more or less) open because to dlls are part of a softwareproject that is under developmen. Maybe there is a possibility to update the references without to need to recompile the C# program?
#MarkJ: No - binary compatibility brought no success.
Here are the sources:
The VB6-Dll:
Option Explicit
Public Function Increment(var As Integer) As Integer
If Not IsNumeric(var) Then Err.Raise 5
Increment = var + 1
End Function
And here the C# code that tries to use the VB6 Dll:
class Program
{
[DllImport("kernel32.dll", SetLastError = true)]
private static extern IntPtr LoadLibrary(String DllName);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern IntPtr GetProcAddress(IntPtr hModule, byte[] procedureName);
static void Main(string[] args)
{
IntPtr pDll = LoadLibrary(#"P:\dev\Path\to\TestProjekt.dll");
string x = "Increment";
Encoding e = Encoding.GetEncoding("ISO-8859-1");
byte[] b = e.GetBytes(x);
IntPtr pAddressOfFunctionToCall = GetProcAddress(pDll, b);
Increment inc = Increment)Marshal.
GetDelegateForFunctionPointer(pAddressOfFunctionToCall,
typeof(Increment));
int a = inc(5); // <---- Here the AccessViolationException is thrown
return;
}
}
In the meantime I have read any doc I could find but still I don't habe any idea why this ist not working grgrgrgrgr
regards
viktor
Your byte[] b has no terminating null, so isn't a valid unmanaged LPCSTR. I don't understand why you are fiddling about trying to encode the method name by hand, instead of declaring GetProcAddress like this and having the Framework interop code take care of the marshalling for you:
public static extern IntPtr GetProcAddress(IntPtr hModule, [MarshalAs(UnmanagedType.LPStr)] string procedureName);
You must check the return value (pAddressOfFunctionToCall). When it isIntPtr.Zero as I believe you are getting, because your lpProcName argument to GetProcAddress is wrong, then attempting to call through its Delegate wrapper will always give an AccessViolationException.
Also, don't omit to call FreeLibrary on the module handle when you are done.

P/Invoke for shell32.dll's SHMultiFileProperties

I'm not very good with P/Invoke. Can anyone tell me how to declare and use the following shell32.dll function in .NET?
From http://msdn.microsoft.com/en-us/library/bb762230%28VS.85%29.aspx:
HRESULT SHMultiFileProperties(
IDataObject *pdtobj,
DWORD dwFlags
);
It is for displaying the Windows Shell Properties dialog for multiple file system objects.
I already figured out how to use SHObjectProperties for one file or folder:
[DllImport("shell32.dll", SetLastError = true)]
static extern bool SHObjectProperties(uint hwnd, uint shopObjectType, [MarshalAs(UnmanagedType.LPWStr)] string pszObjectName, [MarshalAs(UnmanagedType.LPWStr)] string pszPropertyPage);
public static void ShowDialog(Form parent, FileSystemInfo selected)
{
SHObjectProperties((uint)parent.Handle, (uint)ObjectType.File, selected.FullName, null));
}
enum ObjectType
{
Printer = 0x01,
File = 0x02,
VoumeGuid = 0x04,
}
Can anyone help?
There's an IDataObject interface and a DataObject class in the .NET Framework.
[DllImport("shell32.dll", SetLastError = true)]
static extern int SHMultiFileProperties(IDataObject pdtobj, int flags);
public static void Foo()
{
var pdtobj = new DataObject();
pdtobj.SetFileDropList(new StringCollection { #"C:\Users", #"C:\Windows" });
if (SHMultiFileProperties(pdtobj, 0) != 0 /*S_OK*/)
{
throw new Win32Exception();
}
}
EDIT: I've just compiled and tested this and it works (pops up some dialog with folder appearance settings).
I maybe reading you question incorrectly, but I think you are looking for the extended file properties for files. i.e. opening windows explorer and viewing columns like attributes, owner, copyright, size, date created etc?
There is an API in Shell32 called GetDetailsOf that will provide this information. A starting article on codeproject
Cheers,
John

Categories

Resources