Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

C# - EXE/DLL로부터 추출한 이미지/아이콘의 배경색 투명 처리

지난번에 EXE/DLL로부터 이미지 추출하는 방법을 설명했는데요.

프로그램에 보여지는 리소스(예: 아이콘) 추출하는 방법
; https://www.sysnet.pe.kr/2/0/1524

실제로 해보니, 아이콘은 투명 처리가 잘 되어 있는데 이미지의 경우에는 BMP 파일로 저장되어 불투명 이미지가 나왔습니다. 그래서 투명 처리가 필요했는데요. 그래픽 툴을 사용하는 것보다 명령행 도구가 없나 해서 검색해 보았더니 다음의 도구가 나오는군요. ^^

ImageMagick  - Convert, Edit, and Compose Images
; http://www.imagemagick.org

Windows Binary Release
; http://www.imagemagick.org/script/binary-releases.php#windows

압축 버전도 제공하므로 이를 내려받아 해제합니다. exe 파일이 몇 개 나오는데, 그중에서 convert.exe 실행 파일을 이용하면 됩니다.

예를 들어 BMP 이미지에서 0xFF00FF RGB 색을 투명으로 처리하고 싶다면 이렇게 실행해 줍니다.

convert file.bmp -transparent #ff00ff file.png

그런데, EXE/DLL로부터 추출된 이미지 같은 경우에는 가끔 다음과 같은 식으로 오류가 발생하면서 처리가 안 될 때가 있습니다.

C:temp>convert file.bmp -transparent #ff00ff file.png
convert.exe: Length and filesize do not match `file.bmp' @ error/bmp.c/ReadBMPImage/817. 

혹시나 싶어, convert.exe 폴더에 있는 다른 실행 파일인 imdisplay.exe로 파일을 열어도 역시 마찬가지의 오류가 발생합니다.

IMDisplayDoc function [DoReadImage] reported an error.
IMDisplay.exe: length and filesize do not match '...' @ error/bmp.c/ReadBMPImage/817

가만보니, palette가 구성된 이미지인 경우인데 Visual Studio로 확인하면 다음과 같이 왼쪽에 팔렛트가 펼쳐집니다.

bmp2png_transparent_1.png

이런 BMP 파일은 그냥 그림판(mspaint.exe)으로 열어서 24bit BMP 파일로 저장해 주고 convert 프로그램을 실행하면 됩니다.




ImageMagick의 convert.exe에는 많은 옵션이 있지만 투명 처리 정도는 C#으로도 간단히 할 수 있습니다. 다음은 이에 대한 코드를 담고 있는 글입니다.

Setting transparency in an image
; http://blogs.msdn.com/b/jmstall/archive/2007/08/06/setting-transparency-in-an-image.aspx

나중에 찾아 보기 쉽도록 여기다 코드를 그대로 복사해 두죠. ^^

// Simple tool to mark a color on the bitmap transparent.
// http://blogs.msdn.com/jmstall
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.IO;

namespace MakeTransparent
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                Console.WriteLine(
@"Usage:
    MakeTransparent <filename>

Makes the background color in the image transparent. 
This assumes the pixel at 0,0 is the background color. 

This saves it as a new file appended with '.t', so that it doesn't erase 
the original filename.  For example 'c:\dir\a.bmp' becomes 'c:\dir\a.t.bmp'.
Callers can rename as needed.
");
                return;
            }

            string filename = args[0];
            filename = Path.GetFullPath(filename);
            Console.WriteLine("Loading image from:{0}", filename);

            Bitmap myBitmap = new Bitmap(filename);

            // Get the color of a background pixel.
            // Assume upper left corner is opaque.
            Color backColor = myBitmap.GetPixel(0, 0);

            Console.WriteLine("Choosing background color based of pixel at (0,0). Color ={0}", backColor.ToString());

            // Make backColor transparent for myBitmap. This is the heart of the program.
            myBitmap.MakeTransparent(backColor);

            // Change "c:\dir\thing.bmp" to "c:\dir\thing.t.bmp"
            string left = Path.ChangeExtension(filename, null);
            string ext = Path.GetExtension(filename); // includes period ".bmp"
                        
            string outFile = left + ".t" + ext;
            Console.WriteLine("Saving back to file: {0}", outFile);

            myBitmap.Save(outFile);
        }
    }
}

주석에도 달려 있지만, (0,0) 위치의 색을 투명색으로 지정할 뿐 별다르게 특별한 처리는 없습니다.




// https://twitter.com/PR0GRAMMERHUM0R/status/1773122861487575208/photo/1

from rembg import remove
from PIL import Image

input = Image.open('cl.jpg')
output = remove(input)
output.save('output.png')




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 3/29/2024]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 



2018-12-18 08시10분
[초보입니다] 안녕하세요 다름이 아니라..

아이콘 만들기에 대해 자세히 알고 계신거 같아서 질문드립니다..

저는 해당 exe경로로부터 절대경로든 상대 경로든 일단 프로세스 라이브러리를 통해 실행파일을 만들었으나 해당 exe파일도 절대경로 혹은 상대경로에 따른 exe경로의 ico도 같이 들고와 적용 시키려 합니다... 이것이 가능할까요??
[guest]
2018-12-18 08시12분
[초보입니다] 혹은.. 바로가기 만드는 방법으로 바로가기를 생성했으나..

해당 생성 exe의 속성에 바로가기 탭에 파일경로가 다 들어나기에 해당 서버로 공유 스토리지가 사용자에게 들어납니다..

이것을 제어 할 수 있는 방법중 둘중 어느것이 더 좋을지 잘모르겠으나 방법 또한 찾기가 힘듭니다.. 도움을 주셧으면 감사하겠습니다
[guest]
2018-12-18 10시00분
죄송한데요, 질문의 의도를 전혀 모르겠습니다. 다시, 제가 전혀 모르는 사람이라고 생각하고 질문해 주세요.
정성태
2018-12-18 12시56분
[초보입니다] 죄송합니다.
일단 하고자하는 목표는 이렇습니다.
AD정책을 사용하여 exe 포터블 파일를 사용자에게 날리려 합니다.
로그온 스크립트 사용하여 해당 서버로부터 즉 batch 파일의 형식의 파일을 해당 cmd 명령어 copy로 USER로 전달하는 형식입니다.

해당 파일 크기도 크고 용량을 최대한 줄이기 위해서 바로가기를 만들어 바로가기 아이콘 탭의 바로가기 - 파일 위치 열기를 누르면 해당 서버의 공유 폴더에 접근이 되기에 우회 할 수 없도록 코딩을 하고싶은게 저의 목표입니다.

그래서 고안한게..

using IWshRuntimeLibrary;
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace ConsoleApp1
{
    class Program
    {
       // static string _createInk = Console.ReadLine();
       // static string _executeEXE = Console.ReadLine();
        static void Main(string[] args)
        {
            string a = @"C:\Documents and Settings\OSH\DESKTOP\lol_Test.lnk";
            //string a = $@"C:\Documents and Settings\OSH\DESKTOP\{_createInk}.lnk";
            if (!System.IO.File.Exists(a))
            {
                CreateEXE();
                ExecutionStartLocation();
            }
            else
            {
                ExecutionStartLocation();
            }
        }
        public static void CreateEXE()
        {
            Process[] processes = Process.GetProcessesByName("ConsoleApp1");
            foreach (var process in processes)
            {
                //dll, exe 모듈을 가져옵니다.
                ProcessModule mainModule = process.MainModule;
                string fileName = mainModule.FileName.ToString();

                string path = Environment.GetFolderPath(Environment.SpecialFolder.DesktopDirectory);
                var wsh = new WshShell();
                IWshRuntimeLibrary.IWshShortcut myShortcut;
                myShortcut = (IWshRuntimeLibrary.IWshShortcut)wsh.CreateShortcut(path + "/lol_Test.lnk");


                myShortcut.TargetPath = fileName; //@"C:\Users\OSH\source\repos\ConsoleApp1\ConsoleApp1\bin\Debug\ConsoleApp1.exe";
                myShortcut.Description = "osh test";
                myShortcut.IconLocation = @"%ProgramFiles(x86)%\Google\Chrome\Application\chrome.exe";
                //myShortcut.IconLocation = $@"D:\lol\LeagueClient.exe";
                //myShortcut.IconLocation = $@"D:\lol\{_executeEXE}.exe";
                //@"c:\reg\assign.ico";
                myShortcut.Save();
            }

            
        }
        public static void ExecutionStartLocation()
        {
            System.Diagnostics.Process ps = new System.Diagnostics.Process();
            //ps.StartInfo.FileName = @"D:\lol\LeagueClient.exe";
            ps.StartInfo.FileName = @"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe";
            //ps.StartInfo.FileName = $@"D:\lol\{_executeEXE}.exe";
            ps.Start();
        }
    }
}

이러한 코딩이 되고.. 공유폴더로 접근하는 방법을 ps.StartInfo.FileName = @"D:\\AD-SEVER\test\LeagueClient.exe"; 이런식으로 접근함으로서도..
사용자에게 실제적 위치를 보여줄 수 없게 해놨습니다.
하지만 단점이 해당 ConsoleApp1.exe이라는 exe가 없으면 실행이 되지않기에.. 더좋은 방법이 없을까하고 질문드렸습니다.
[guest]
2018-12-18 01시16분
그러니까, 간단하게 정리해 보면.

test.exe를 실행하는 바로가기 아이콘을 만들어 놓으면 그 바로가기의 속성으로 test.exe를 찾을 수 있으니까, proxy.exe를 대신 사용자 컴퓨터에 로그온 스크립트로 다운로드하게 만들어 놓고 바로가기 아이콘은 그 proxy.exe를 가리키게 한 후 proxy.exe에서는 내부적으로 test.exe를 실행시키도록 (현재 그렇게) 만들었다는 거죠?

그런데 그 방법이 마음에 들지 않아 proxy.exe를 만들지 않고도 test.exe를 실행하는 바로가기 아이콘을 만들고 싶은데 test.exe임을 숨기는 방법이 없냐고 묻는 것이고요? 맞나요?
정성태
2018-12-18 01시31분
[초보입니다] 네네 정확합니다.
제가 워낙 초보자라 설명법이 너무 부족합니다 ㅠㅠ
[guest]
2018-12-18 02시17분
일단, 바로가기 아이콘으로는 해당 문제를 해결할 수 없습니다. 그렇긴 한데, 나름 재치있게 잘 하셨는데요, 딱히 더 나은 방법이 있을 것 같진 않습니다. 가령 mklink를 이용해 또 다른 symbolic link를 만드는 것도 가능하긴 하지만 그것 역시 속성 창을 통해 원래 파일의 위치가 알려집니다.
정성태
2018-12-18 02시29분
[초보입니다] 아하.. 그렇군요..
속성창을 사용자가 제어를 못하게 막던지..
혹은 기존 바로가기와 같은 기능을 만들어야하는데..
해당 C# 콘솔 어플리케이션 라이브러리에서는 ICON만 떠와서 만들 수 있는 기능없는거같고 바로가기를 만드는 과정이 그냥 있는거같아 두가지중 하나를 나름 해결해야보려 하는데.. 저도 여기까지가 한계인거같아 최대한으로 해보고 질문 드려본거라..
해당 작성된 콘솔 앱과 콘솔로 작성하여 만들어진 바로가기 exe를 같이 들고가는 방법으로 콘솔앱을 숨겨서 가져가는 방법도 있는거같은데..
해당 사항에 대해 윗 선임분한테 여쭤봐야할거같습니다..

다른 방법또한 있으신데 비슷하다 하니 참고한번해보겠습니다 ^^
[guest]

... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13151정성태10/31/20226129C/C++: 161. Windows 11 환경에서 raw socket 테스트하는 방법파일 다운로드1
13150정성태10/30/20226047C/C++: 160. Visual Studio 2022로 빌드한 C++ 프로그램을 위한 다른 PC에서 실행하는 방법
13149정성태10/27/20226030오류 유형: 825. C# - CLR ETW 이벤트 수신이 GCHeapStats_V1/V2에 대해 안 되는 문제파일 다운로드1
13148정성태10/26/20225955오류 유형: 824. msbuild 에러 - error NETSDK1005: Assets file '...\project.assets.json' doesn't have a target for 'net5.0'. Ensure that restore has run and that you have included 'net5.0' in the TargetFramew
13147정성태10/25/20225028오류 유형: 823. Visual Studio 2022 - Unable to attach to CoreCLR. The debugger's protocol is incompatible with the debuggee.
13146정성태10/24/20225879.NET Framework: 2060. C# - Java의 Xmx와 유사한 힙 메모리 최댓값 제어 옵션 HeapHardLimit
13145정성태10/21/20226194오류 유형: 822. db2 - Password validation for user db2inst1 failed with rc = -2146500508
13144정성태10/20/20226080.NET Framework: 2059. ClrMD를 이용해 윈도우 환경의 메모리 덤프로부터 닷넷 모듈을 추출하는 방법파일 다운로드1
13143정성태10/19/20226582오류 유형: 821. windbg/sos - Error code - 0x000021BE
13142정성태10/18/20225909도서: 시작하세요! C# 12 프로그래밍
13141정성태10/17/20227196.NET Framework: 2058. [in,out] 배열을 C#에서 C/C++로 넘기는 방법 - 세 번째 이야기파일 다운로드1
13140정성태10/11/20226542C/C++: 159. C/C++ - 리눅스 환경에서 u16string 문자열을 출력하는 방법 [2]
13139정성태10/9/20226220.NET Framework: 2057. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 모든 닷넷 모듈을 추출하는 방법파일 다운로드1
13138정성태10/8/20227623.NET Framework: 2056. C# - await 비동기 호출을 기대한 메서드가 동기로 호출되었을 때의 부작용 [1]
13137정성태10/8/20225912.NET Framework: 2055. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프로부터 닷넷 모듈을 추출하는 방법
13136정성태10/7/20226492.NET Framework: 2054. .NET Core/5+ SDK 설치 없이 dotnet-dump 사용하는 방법
13135정성태10/5/20226777.NET Framework: 2053. 리눅스 환경의 .NET Core 3/5+ 메모리 덤프를 분석하는 방법 - 두 번째 이야기
13134정성태10/4/20225456오류 유형: 820. There is a problem with AMD Radeon RX 5600 XT device. For more information, search for 'graphics device driver error code 31'
13133정성태10/4/20225837Windows: 211. Windows - (commit이 아닌) reserved 메모리 사용량 확인 방법 [1]
13132정성태10/3/20225731스크립트: 42. 파이썬 - latexify-py 패키지 소개 - 함수를 mathjax 식으로 표현
13131정성태10/3/20228551.NET Framework: 2052. C# - Windows Forms의 데이터 바인딩 지원(DataBinding, DataSource) [2]파일 다운로드1
13130정성태9/28/20225424.NET Framework: 2051. .NET Core/5+ - 에러 로깅을 위한 Middleware가 동작하지 않는 경우파일 다운로드1
13129정성태9/27/20225706.NET Framework: 2050. .NET Core를 IIS에서 호스팅하는 경우 .NET Framework CLR이 함께 로드되는 환경
13128정성태9/23/20228396C/C++: 158. Visual C++ - IDL 구문 중 "unsigned long"을 인식하지 못하는 #import파일 다운로드1
13127정성태9/22/20226870Windows: 210. WSL에 systemd 도입
13126정성태9/15/20227475.NET Framework: 2049. C# 11 - 정적 메서드에 대한 delegate 처리 시 cache 적용
... 16  17  18  19  [20]  21  22  23  24  25  26  27  28  29  30  ...