성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
[정성태] 만드실 수 있습니다. 단지, Unity 엔진 내의 스크립트와 W...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>Reg-free COM 개체 사용을 위한 manifest 파일 생성 도구 - COMRegFreeManifest</h1> <p> COM 객체를 regsvr32.exe로 등록하지 않아도, manifest 파일을 통해 직접 생성해 사용할 수 있다고 설명했었습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Registry 등록 없이 COM 개체 사용 ; <a target='tab' href='https://www.sysnet.pe.kr/2/1/262'>https://www.sysnet.pe.kr/2/1/262</a> </pre> <br /> 일례로 다음의 글들이 모두 그런 식으로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C# - VLC(ActiveX) 컨트롤을 레지스트리 등록없이 사용하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/1640'>https://www.sysnet.pe.kr/2/0/1640</a> eBEST XingAPI의 C# 래퍼 버전 - XingAPINet Nuget 패키지 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12134'>https://www.sysnet.pe.kr/2/0/12134</a> C# - 키움 Open API+ 사용 시 Registry 등록 없이 KHOpenAPI.ocx 사용하는 방법 ; <a target='tab' href='https://www.sysnet.pe.kr/2/0/12129'>https://www.sysnet.pe.kr/2/0/12129</a> </pre> <br /> COM 객체를 등록하지 않고 같은 폴더에 있는 dll로부터 로드하고 있습니다. 그런데... 가끔 해보는 거지만 그때마다 manifest 파일을 일일이 만들려니 꽤나... ^^; 귀찮습니다. 그래서 어차피, tlbimp.exe가 생성한 COM Interop DLL만 있으면 나머지는 Reflection으로 COM 객체의 정보를 열람할 수 있기 때문에 manifest 만드는 것을 자동화했습니다.<br /> <br /> 다음 코드는 간략하게 만들어 본 것이고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using System.Text; namespace COMRegFreeManifest { class Program { static void Main(string[] args) { if (args.Length < 1) { Console.WriteLine("COMRegFreeManifest.exe [com_dll_path]"); return; } string path = args[0]; string currentPath = Environment.CurrentDirectory; string dllManifestPath = Path.Combine(currentPath, Path.GetFileName(path)); string exeManifestPath = Path.Combine(currentPath, "Sample.exe"); string impLibPath = Path.Combine(Path.GetTempPath(), "test.dll"); try { if (RunTlbImp(path, impLibPath) == false) { Console.WriteLine("TLBIMP not work"); return; } byte[] buf = File.ReadAllBytes(impLibPath); Assembly asm = Assembly.ReflectionOnlyLoad(buf); ExportDllManifest(asm, impLibPath, dllManifestPath); ExportExeManifest(asm, impLibPath, dllManifestPath, exeManifestPath); } finally { if (File.Exists(impLibPath) == true) { File.Delete(impLibPath); } } } private static void ExportExeManifest(Assembly asm, string impLibPath, string dllManifestPath, string exeManifestPath) { string manifestTemplate = @"<?xml version=""1.0"" encoding=""utf-8""?> <asmv1:assembly manifestVersion=""1.0"" xmlns=""urn:schemas-microsoft-com:asm.v1"" xmlns:asmv1=""urn:schemas-microsoft-com:asm.v1"" xmlns:asmv2=""urn:schemas-microsoft-com:asm.v2"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""> <assemblyIdentity version=""1.0.0.0"" name=""MyApplication.app""/> <trustInfo xmlns=""urn:schemas-microsoft-com:asm.v2""> <security> <requestedPrivileges xmlns=""urn:schemas-microsoft-com:asm.v3""> <requestedExecutionLevel level=""asInvoker"" uiAccess=""false"" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns=""urn:schemas-microsoft-com:compatibility.v1""> <application> </application> </compatibility> <dependency> <dependentAssembly asmv2:dependencyType=""install"" asmv2:codebase=""{0}.manifest""> <assemblyIdentity name=""{0}"" version=""{1}"" type=""win32"" /> </dependentAssembly> </dependency> </asmv1:assembly> "; string dllName = Path.GetFileName(dllManifestPath); string version = asm.GetName().Version.ToString(); string manifestText = string.Format(manifestTemplate, dllName, version); File.WriteAllText(exeManifestPath + ".manifest", manifestText); } private static void ExportDllManifest(Assembly asm, string impLibPath, string dllManifestPath) { string txt = GetManifestContents(asm, dllManifestPath); File.WriteAllText(dllManifestPath + ".manifest", txt); } static string GetManifestContents(Assembly asm, string dllManifestPath) { string manifestTemplate = @"<?xml version=""1.0"" encoding=""UTF-8"" standalone=""yes""?> <assembly xmlns=""urn:schemas-microsoft-com:asm.v1"" manifestVersion=""1.0""> <assemblyIdentity version=""{0}"" name=""{1}"" type=""win32""> </assemblyIdentity> <file name=""{1}""> <comClass clsid=""{2}"" threadingModel=""Apartment"" tlbid=""{3}"" /> <typelib tlbid=""{3}"" version=""{4}"" helpdir="""" resourceid=""0"" /> </file> {5} </assembly> "; string interfaceTemplate = @" <comInterfaceExternalProxyStub name=""{0}"" iid=""{1}"" proxyStubClsid32=""{{00020424-0000-0000-C000-000000000046}}"" baseInterface=""{2}"" tlbid=""{3}""> </comInterfaceExternalProxyStub> "; string version = asm.GetName().Version.ToString(); string dllName = Path.GetFileName(dllManifestPath); string clsid = $"{{{GetClsid(asm)}}}"; string tlbid = $"{{{GetAssemblyAttr(asm, typeof(GuidAttribute))}}}"; string tlbVersion = GetTypeLibVersion(asm); StringBuilder sb = new StringBuilder(); foreach (Type type in asm.GetTypes()) { if (type.IsInterface == false || HasCoClassAttribute(type)) { continue; } string interfaceName = type.Name; string interfaceIid = $"{{{GetGuid(type)}}}"; string baseInterfaceIid = GetBaseInterfaceIID(type); string interfaceText = string.Format(interfaceTemplate, interfaceName, interfaceIid, baseInterfaceIid, tlbid); sb.AppendLine(interfaceText); } string manifestText = string.Format(manifestTemplate, version, dllName, clsid, tlbid, tlbVersion, sb.ToString()); return manifestText; } private static string GetBaseInterfaceIID(Type type) { foreach (CustomAttributeData attr in type.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == typeof(TypeLibTypeAttribute)) { TypeLibTypeFlags flags = (TypeLibTypeFlags)attr.ConstructorArguments[0].Value; if (flags.HasFlag(TypeLibTypeFlags.FDual)) { return "{00020400-0000-0000-C000-000000000046}"; } else { return "{00000000-0000-0000-C000-000000000046}"; } } } return ""; } private static string GetTypeLibVersion(Assembly asm) { foreach (CustomAttributeData attr in asm.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == typeof(TypeLibVersionAttribute)) { return $"{attr.ConstructorArguments[0].Value}.{attr.ConstructorArguments[1].Value}"; } } return ""; } private static bool HasCoClassAttribute(Type type) { foreach (CustomAttributeData attr in type.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == typeof(CoClassAttribute)) { return true; } } return false; } private static string GetClsid(Assembly asm) { foreach (Type type in asm.GetTypes()) { foreach (CustomAttributeData attr in type.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == typeof(CoClassAttribute)) { return GetGuid(attr.ConstructorArguments[0].Value as Type); } } } return ""; } private static string GetGuid(Type type) { foreach (CustomAttributeData attr in type.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == typeof(GuidAttribute)) { return attr.ConstructorArguments[0].Value as string; } } return ""; } private static string GetAssemblyAttr(Assembly asm, Type targetType) { foreach (CustomAttributeData attr in asm.GetCustomAttributesData()) { if (attr.Constructor.DeclaringType == targetType) { return attr.ConstructorArguments[0].Value as string; } } return ""; } private static bool RunTlbImp(string path, string impLibPath) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = GetTlbImpPath("tlbimp.exe"); psi.Arguments = $"\"{path}\" /out:\"{impLibPath}\""; psi.CreateNoWindow = true; psi.UseShellExecute = false; Process newProc = Process.Start(psi); newProc.WaitForExit(); return File.Exists(impLibPath); } private static string GetTlbImpPath(string toolName) { using (RegistryKey regKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\WOW6432Node\Microsoft\Microsoft SDKs\NETFXSDK", false)) { foreach (string version in regKey.GetSubKeyNames()) { using (RegistryKey versionKey = regKey.OpenSubKey(version, false)) { foreach (string tool in versionKey.GetSubKeyNames()) { using (RegistryKey toolKey = versionKey.OpenSubKey(tool)) { string path = toolKey.GetValue("InstallationFolder") as string; string tlbimpPath = Path.Combine(path, toolName); if (File.Exists(tlbimpPath) == true) { return tlbimpPath; } } } } } } return null; } } } </pre> <br /> 실행은 단순하게, COM DLL 파일을 전달하면 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > c:\temp> COMRegFreeManifest.exe c:\test\my_atl.dll </pre> <br /> 그럼 my_atl.dll.manifest와 Sample.exe.manifest 파일이 각각 다음과 같은 식으로 생성됩니다.<br /> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>[my_atl.dll.manifest]</div> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <?xml version="1.0" encoding="UTF-8" standalone="yes"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <assemblyIdentity version="1.0.0.0" name="my_atl.dll" type="win32"> </assemblyIdentity> <file name="my_atl.dll"> <comClass clsid="{2F71B57A-B6DF-4733-A29A-6ECA13FAE34F}" threadingModel="Apartment" tlbid="{0B2AAC68-8E4B-4BAA-85D7-4DF62A224D9F}" /> <typelib tlbid="{0B2AAC68-8E4B-4BAA-85D7-4DF62A224D9F}" version="1.0" helpdir="" resourceid="0" /> </file> <comInterfaceExternalProxyStub name="IATLSimpleObject" iid="{CB82A462-8F49-4434-987B-CB8FBC8A9115}" proxyStubClsid32="{00020424-0000-0000-C000-000000000046}" baseInterface="{00020400-0000-0000-C000-000000000046}" tlbid="{0B2AAC68-8E4B-4BAA-85D7-4DF62A224D9F}"> </comInterfaceExternalProxyStub> </assembly> </pre> <br /> <br /><div style='font-size: 12pt; font-family: Malgun Gothic, Consolas; color: #2211AA; text-align: left; font-weight: bold'>[Sample.exe.manifest]</div> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <?xml version="1.0" encoding="utf-8"?> <asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <assemblyIdentity version="1.0.0.0" name="MyApplication.app"/> <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2"> <security> <requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3"> <requestedExecutionLevel level="asInvoker" uiAccess="false" /> </requestedPrivileges> </security> </trustInfo> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> </application> </compatibility> <dependency> <dependentAssembly asmv2:dependencyType="install" asmv2:codebase="my_atl.dll.manifest"> <assemblyIdentity name="my_atl.dll" version="1.0.0.0" type="win32" /> </dependentAssembly> </dependency> </asmv1:assembly> </pre> <br /> 오~~~ 제법 폼 나는군요. ^^ 이제 my_atl.dll.manifest 파일은 EXE 프로젝트에 포함해 "Copy to Output Directory" 옵션을 "Copy if newer"로 설정하고 Sample.exe.manifest 파일은 EXE 프로젝트 속성 창의 "Application" 범주의 "Resources" 영역에 "Manifest:" 콤보 박스에 등록해 주시면 됩니다.<br /> <br /> (전체 프로젝트는 github에 올렸습니다. - <a target='tab' href='https://github.com/stjeong/DotNetSamples/tree/master/WinConsole/COMRegFreeManifest'>https://github.com/stjeong/DotNetSamples/tree/master/WinConsole/COMRegFreeManifest</a>)<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
5760
(왼쪽의 숫자를 입력해야 합니다.)