I created a MSN Group, CAD & Development MSN Group:
http://caddev.groups.live.com
We talk about CAD secondary development technology, e.g. UG/NX, ProE, AutoCAD, etc.
Welcome to join us!
Begtostudy's Technology Blog
Welcome to my Technology Blog!
2011-03-09
CAD & Development MSN Group
2011-03-03
Use CLR4 Hosting API to invoke .NET assembly from native C++ (Reprint)
The Common Language Runtime (CLR) allows a level of integration between itself and a host. This article is about a C++ code sample that demonstrates using the Hosting Interfaces of .NET Framework 4.0 to host a specific version of CLR in the process, load a .NET assembly, and invoke the types in the assembly. The code sample also demonstrates the new In-Process Side-by-Side feature in .NET Framework 4. The .NET Framework 4 runtime, and all future runtimes, are able to run in-process with one another. .NET Framework 4 runtime and beyond are also able to run in-process with any single older runtime. In ther words, you will be able to load 4, 5 and 2.0 in the same process, but you will not be able to load 1.1 and 2.0 in the same process. The code sample hosts .NET runtime 4.0 and 2.0 side by side, and loads a .NET 2.0 assembly into the two runtimes.
The code sample in this article (CppHostCLR) can be downloaded from Microsoft All-In-One Code Framework, which is a centralized code sample library.
The following steps walk through a demonstration of the CLR Hosting sample.
Step1. After you successfully build the sample project, and the dependent .NET class library projects (CSClassLibrary and CSNET2ClassLibrary) in Visual Studio 2010, you will get an application and two libraries: CppHostCLR.exe, CSClassLibrary.dll, and CSNET2ClassLibrary.dll. Make sure that the files are in the same folder.
Step2. Run the application in a command prompt. The application demonstrates the CLR In-Process Side-by-Side feature that is new in .NET 4 first. It hosts the .NET runtime 4.0 first and loads a .NET 2.0 assembly into the runtime to invoke its types. Then the application hosts the .NET runtime 2.0 side by side with the .NET runtime 4.0 and loads the same .NET 2.0 assembly into the .NET 4.0 runtime. If the operations succeed, the application prints the
following content in the console.
Load and start the .NET runtime v4.0.30319
Load the assembly CSNET2ClassLibrary
Call CSNET2ClassLibrary.CSSimpleObject.GetStringLength("HelloWorld") => 10
Call CSNET2ClassLibrary.CSSimpleObject.ToString() => 0.00
Load and start the .NET runtime v2.0.50727
Load the assembly CSNET2ClassLibrary
Call CSNET2ClassLibrary.CSSimpleObject.GetStringLength("HelloWorld") => 10
Call CSNET2ClassLibrary.CSSimpleObject.ToString() => 0.00
Presss ENTER to continue ...
You can verify that both .NET 2.0 and .NET 4.0 runtimes are loaded by using Process Explorer (http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx). In Lower Pane View / DLLs of the tool, you can see all modules loaded in the process. If clr.dll (the .NET 4 runtime module) and mscorwks.dll (the .NET 2 runtime module) are in the list, both .NET 2.0 and .NET 4.0 runtimes are loaded.
Step3. Press ENTER to continue. The application will host the .NET runtime 4.0 and use the ICLRRuntimeHost interface that was provided in .NET v2.0 to
load a .NET 4.0 assembly and invoke its type.
Load and start the .NET runtime v4.0.30319
Load the assembly CSClassLibrary.dll
Call CSClassLibrary.CSSimpleObject.GetStringLength("HelloWorld") => 10
The key code in the code sample is attached below.
1: //
2: // FUNCTION: RuntimeHostV4Demo1(PCWSTR, PCWSTR)
3: //
4: // PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting
5: // Interfaces to host a .NET runtime, and use the ICorRuntimeHost interface
6: // that was provided in .NET v1.x to load a .NET assembly and invoke its
7: // type.
8: //
9: // If the .NET runtime specified by the pszVersion parameter cannot be
10: // loaded into the current process, the function prints ".NET runtime cannot be loaded", and return.
11: //
12: // If the .NET runtime is successfully loaded, the function loads the
13: // assembly identified by the pszAssemblyName parameter. Next, the function
14: // instantiates the class (pszClassName) in the assembly, calls its
15: // ToString() member method, and print the result. Last, the demo invokes
16: // the public static function 'int GetStringLength(string str)' of the class
17: // and print the result too.
18: //
19: // PARAMETERS:
20: // * pszVersion - The desired DOTNETFX version, in the format “°vX.X.XXXXX”±.
21: // The parameter must not be NULL. It’ˉs important to note that this
22: // parameter should match exactly the directory names for each version of
23: // the framework, under C:\Windows\Microsoft.NET\Framework[64]. The
24: // current possible values are "v1.0.3705", "v1.1.4322", "v2.0.50727" and
25: // "v4.0.30319". Also, note that the “°v”± prefix is mandatory.
26: // * pszAssemblyName - The display name of the assembly to be loaded, such
27: // as "CSClassLibrary". The ".DLL" file extension is not appended.
28: // * pszClassName - The name of the Type that defines the method to invoke.
29: //
30: // RETURN VALUE: HRESULT of the demo.
31: //
32: HRESULT RuntimeHostV4Demo1(PCWSTR pszVersion, PCWSTR pszAssemblyName,
33: PCWSTR pszClassName)
34: {
35: HRESULT hr;
36: ICLRMetaHost *pMetaHost = NULL;
37: ICLRRuntimeInfo *pRuntimeInfo = NULL;
38: // ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces
39: // supported by CLR 4.0. Here we demo the ICorRuntimeHost interface that
40: // was provided in .NET v1.x, and is compatible with all .NET Frameworks.
41: ICorRuntimeHost *pCorRuntimeHost = NULL;
42: IUnknownPtr spAppDomainThunk = NULL;
43: _AppDomainPtr spDefaultAppDomain = NULL;
44: // The .NET assembly to load.
45: bstr_t bstrAssemblyName(pszAssemblyName);
46: _AssemblyPtr spAssembly = NULL;
47: // The .NET class to instantiate.
48: bstr_t bstrClassName(pszClassName);
49: _TypePtr spType = NULL;
50: variant_t vtObject;
51: variant_t vtEmpty;
52: // The static method in the .NET class to invoke.
53: bstr_t bstrStaticMethodName(L"GetStringLength");
54: SAFEARRAY *psaStaticMethodArgs = NULL;
55: variant_t vtStringArg(L"HelloWorld");
56: variant_t vtLengthRet;
57: // The instance method in the .NET class to invoke.
58: bstr_t bstrMethodName(L"ToString");
59: SAFEARRAY *psaMethodArgs = NULL;
60: variant_t vtStringRet;
61: //
62: // Load and start the .NET runtime.
63: //
64: wprintf(L"Load and start the .NET runtime %s \n", pszVersion);
65: hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
66: if (FAILED(hr))
67: {
68: wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
69: goto Cleanup;
70: }
71: // Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
72: // supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
73: hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
74: if (FAILED(hr))
75: {
76: wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
77: goto Cleanup;
78: }
79: // Check if the specified runtime can be loaded into the process. This
80: // method will take into account other runtimes that may already be
81: // loaded into the process and set pbLoadable to TRUE if this runtime can
82: // be loaded in an in-process side-by-side fashion.
83: BOOL fLoadable;
84: hr = pRuntimeInfo->IsLoadable(&fLoadable);
85: if (FAILED(hr))
86: {
87: wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
88: goto Cleanup;
89: }
90: if (!fLoadable)
91: {
92: wprintf(L".NET runtime %s cannot be loaded\n", pszVersion);
93: goto Cleanup;
94: }
95: // Load the CLR into the current process and return a runtime interface
96: // pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
97: // interfaces supported by CLR 4.0. Here we demo the ICorRuntimeHost
98: // interface that was provided in .NET v1.x, and is compatible with all
99: // .NET Frameworks.
100: hr = pRuntimeInfo->GetInterface(CLSID_CorRuntimeHost,
101: IID_PPV_ARGS(&pCorRuntimeHost));
102: if (FAILED(hr))
103: {
104: wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
105: goto Cleanup;
106: }
107: // Start the CLR.
108: hr = pCorRuntimeHost->Start();
109: if (FAILED(hr))
110: {
111: wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
112: goto Cleanup;
113: }
114: //
115: // Load the NET assembly. Call the static method GetStringLength of the
116: // class CSSimpleObject. Instantiate the class CSSimpleObject and call
117: // its instance method ToString.
118: //
119: // The following C++ code does the same thing as this C# code:
120: //
121: // Assembly assembly = AppDomain.CurrentDomain.Load(pszAssemblyName);
122: // object length = type.InvokeMember("GetStringLength",
123: // BindingFlags.InvokeMethod | BindingFlags.Static |
124: // BindingFlags.Public, null, null, new object[] { "HelloWorld" });
125: // object obj = assembly.CreateInstance("CSClassLibrary.CSSimpleObject");
126: // object str = type.InvokeMember("ToString",
127: // BindingFlags.InvokeMethod | BindingFlags.Instance |
128: // BindingFlags.Public, null, obj, new object[] { });
129: // Get a pointer to the default AppDomain in the CLR.
130: hr = pCorRuntimeHost->GetDefaultDomain(&spAppDomainThunk);
131: if (FAILED(hr))
132: {
133: wprintf(L"ICorRuntimeHost::GetDefaultDomain failed w/hr 0x%08lx\n", hr);
134: goto Cleanup;
135: }
136: hr = spAppDomainThunk->QueryInterface(IID_PPV_ARGS(&spDefaultAppDomain));
137: if (FAILED(hr))
138: {
139: wprintf(L"Failed to get default AppDomain w/hr 0x%08lx\n", hr);
140: goto Cleanup;
141: }
142: // Load the .NET assembly.
143: wprintf(L"Load the assembly %s\n", pszAssemblyName);
144: hr = spDefaultAppDomain->Load_2(bstrAssemblyName, &spAssembly);
145: if (FAILED(hr))
146: {
147: wprintf(L"Failed to load the assembly w/hr 0x%08lx\n", hr);
148: goto Cleanup;
149: }
150: // Get the Type of CSSimpleObject.
151: hr = spAssembly->GetType_2(bstrClassName, &spType);
152: if (FAILED(hr))
153: {
154: wprintf(L"Failed to get the Type interface w/hr 0x%08lx\n", hr);
155: goto Cleanup;
156: }
157: // Call the static method of the class:
158: // public static int GetStringLength(string str);
159: // Create a safe array to contain the arguments of the method. The safe
160: // array must be created with vt = VT_VARIANT because .NET reflection
161: // expects an array of Object - VT_VARIANT. There is only one argument,
162: // so cElements = 1.
163: psaStaticMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 1);
164: LONG index = 0;
165: hr = SafeArrayPutElement(psaStaticMethodArgs, &index, &vtStringArg);
166: if (FAILED(hr))
167: {
168: wprintf(L"SafeArrayPutElement failed w/hr 0x%08lx\n", hr);
169: goto Cleanup;
170: }
171: // Invoke the "GetStringLength" method from the Type interface.
172: hr = spType->InvokeMember_3(bstrStaticMethodName, static_cast(
173: BindingFlags_InvokeMethod | BindingFlags_Static | BindingFlags_Public),
174: NULL, vtEmpty, psaStaticMethodArgs, &vtLengthRet);
175: if (FAILED(hr))
176: {
177: wprintf(L"Failed to invoke GetStringLength w/hr 0x%08lx\n", hr);
178: goto Cleanup;
179: }
180: // Print the call result of the static method.
181: wprintf(L"Call %s.%s(\"%s\") => %d\n",
182: static_cast(bstrClassName),
183: static_cast(bstrStaticMethodName),
184: static_cast(vtStringArg.bstrVal),
185: vtLengthRet.lVal);
186: // Instantiate the class.
187: hr = spAssembly->CreateInstance(bstrClassName, &vtObject);
188: if (FAILED(hr))
189: {
190: wprintf(L"Assembly::CreateInstance failed w/hr 0x%08lx\n", hr);
191: goto Cleanup;
192: }
193: // Call the instance method of the class.
194: // public string ToString();
195: // Create a safe array to contain the arguments of the method.
196: psaMethodArgs = SafeArrayCreateVector(VT_VARIANT, 0, 0);
197: // Invoke the "ToString" method from the Type interface.
198: hr = spType->InvokeMember_3(bstrMethodName, static_cast(
199: BindingFlags_InvokeMethod | BindingFlags_Instance | BindingFlags_Public),
200: NULL, vtObject, psaMethodArgs, &vtStringRet);
201: if (FAILED(hr))
202: {
203: wprintf(L"Failed to invoke ToString w/hr 0x%08lx\n", hr);
204: goto Cleanup;
205: }
206: // Print the call result of the method.
207: wprintf(L"Call %s.%s() => %s\n",
208: static_cast(bstrClassName),
209: static_cast(bstrMethodName),
210: static_cast(vtStringRet.bstrVal));
211: Cleanup:
212: if (pMetaHost)
213: {
214: pMetaHost->Release();
215: pMetaHost = NULL;
216: }
217: if (pRuntimeInfo)
218: {
219: pRuntimeInfo->Release();
220: pRuntimeInfo = NULL;
221: }
222: if (pCorRuntimeHost)
223: {
224: // Please note that after a call to Stop, the CLR cannot be
225: // reinitialized into the same process. This step is usually not
226: // necessary. You can leave the .NET runtime loaded in your process.
227: //wprintf(L"Stop the .NET runtime\n");
228: //pCorRuntimeHost->Stop();
229: pCorRuntimeHost->Release();
230: pCorRuntimeHost = NULL;
231: }
232: if (psaStaticMethodArgs)
233: {
234: SafeArrayDestroy(psaStaticMethodArgs);
235: psaStaticMethodArgs = NULL;
236: }
237: if (psaMethodArgs)
238: {
239: SafeArrayDestroy(psaMethodArgs);
240: psaMethodArgs = NULL;
241: }
242: return hr;
243: }
244:
245: //
246: // FUNCTION: RuntimeHostV4Demo2(PCWSTR, PCWSTR)
247: //
248: // PURPOSE: The function demonstrates using .NET Framework 4.0 Hosting
249: // Interfaces to host a .NET runtime, and use the ICLRRuntimeHost interface
250: // that was provided in .NET v2.0 to load a .NET assembly and invoke its
251: // type. Because ICLRRuntimeHost is not compatible with .NET runtime v1.x,
252: // the requested runtime must not be v1.x.
253: //
254: // If the .NET runtime specified by the pszVersion parameter cannot be
255: // loaded into the current process, the function prints ".NET runtime cannot be loaded", and return.
256: //
257: // If the .NET runtime is successfully loaded, the function loads the
258: // assembly identified by the pszAssemblyName parameter. Next, the function
259: // invokes the public static function 'int GetStringLength(string str)' of
260: // the class and print the result.
261: //
262: // PARAMETERS:
263: // * pszVersion - The desired DOTNETFX version, in the format “°vX.X.XXXXX”±.
264: // The parameter must not be NULL. It’ˉs important to note that this
265: // parameter should match exactly the directory names for each version of
266: // the framework, under C:\Windows\Microsoft.NET\Framework[64]. Because
267: // the ICLRRuntimeHost interface does not support the .NET v1.x runtimes,
268: // the current possible values of the parameter are "v2.0.50727" and
269: // "v4.0.30319". Also, note that the “°v”± prefix is mandatory.
270: // * pszAssemblyPath - The path to the Assembly to be loaded.
271: // * pszClassName - The name of the Type that defines the method to invoke.
272: //
273: // RETURN VALUE: HRESULT of the demo.
274: //
275: HRESULT RuntimeHostV4Demo2(PCWSTR pszVersion, PCWSTR pszAssemblyPath,
276: PCWSTR pszClassName)
277: {
278: HRESULT hr;
279: ICLRMetaHost *pMetaHost = NULL;
280: ICLRRuntimeInfo *pRuntimeInfo = NULL;
281: // ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting interfaces
282: // supported by CLR 4.0. Here we demo the ICLRRuntimeHost interface that
283: // was provided in .NET v2.0 to support CLR 2.0 new features.
284: // ICLRRuntimeHost does not support loading the .NET v1.x runtimes.
285: ICLRRuntimeHost *pClrRuntimeHost = NULL;
286: // The static method in the .NET class to invoke.
287: PCWSTR pszStaticMethodName = L"GetStringLength";
288: PCWSTR pszStringArg = L"HelloWorld";
289: DWORD dwLengthRet;
290: //
291: // Load and start the .NET runtime.
292: //
293: wprintf(L"Load and start the .NET runtime %s \n", pszVersion);
294: hr = CLRCreateInstance(CLSID_CLRMetaHost, IID_PPV_ARGS(&pMetaHost));
295: if (FAILED(hr))
296: {
297: wprintf(L"CLRCreateInstance failed w/hr 0x%08lx\n", hr);
298: goto Cleanup;
299: }
300: // Get the ICLRRuntimeInfo corresponding to a particular CLR version. It
301: // supersedes CorBindToRuntimeEx with STARTUP_LOADER_SAFEMODE.
302: hr = pMetaHost->GetRuntime(pszVersion, IID_PPV_ARGS(&pRuntimeInfo));
303: if (FAILED(hr))
304: {
305: wprintf(L"ICLRMetaHost::GetRuntime failed w/hr 0x%08lx\n", hr);
306: goto Cleanup;
307: }
308: // Check if the specified runtime can be loaded into the process. This
309: // method will take into account other runtimes that may already be
310: // loaded into the process and set pbLoadable to TRUE if this runtime can
311: // be loaded in an in-process side-by-side fashion.
312: BOOL fLoadable;
313: hr = pRuntimeInfo->IsLoadable(&fLoadable);
314: if (FAILED(hr))
315: {
316: wprintf(L"ICLRRuntimeInfo::IsLoadable failed w/hr 0x%08lx\n", hr);
317: goto Cleanup;
318: }
319: if (!fLoadable)
320: {
321: wprintf(L".NET runtime %s cannot be loaded\n", pszVersion);
322: goto Cleanup;
323: }
324: // Load the CLR into the current process and return a runtime interface
325: // pointer. ICorRuntimeHost and ICLRRuntimeHost are the two CLR hosting
326: // interfaces supported by CLR 4.0. Here we demo the ICLRRuntimeHost
327: // interface that was provided in .NET v2.0 to support CLR 2.0 new
328: // features. ICLRRuntimeHost does not support loading the .NET v1.x
329: // runtimes.
330: hr = pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost,
331: IID_PPV_ARGS(&pClrRuntimeHost));
332: if (FAILED(hr))
333: {
334: wprintf(L"ICLRRuntimeInfo::GetInterface failed w/hr 0x%08lx\n", hr);
335: goto Cleanup;
336: }
337: // Start the CLR.
338: hr = pClrRuntimeHost->Start();
339: if (FAILED(hr))
340: {
341: wprintf(L"CLR failed to start w/hr 0x%08lx\n", hr);
342: goto Cleanup;
343: }
344: //
345: // Load the NET assembly and call the static method GetStringLength of
346: // the type CSSimpleObject in the assembly.
347: //
348: wprintf(L"Load the assembly %s\n", pszAssemblyPath);
349: // The invoked method of ExecuteInDefaultAppDomain must have the
350: // following signature: static int pwzMethodName (String pwzArgument)
351: // where pwzMethodName represents the name of the invoked method, and
352: // pwzArgument represents the string value passed as a parameter to that
353: // method. If the HRESULT return value of ExecuteInDefaultAppDomain is
354: // set to S_OK, pReturnValue is set to the integer value returned by the
355: // invoked method. Otherwise, pReturnValue is not set.
356: hr = pClrRuntimeHost->ExecuteInDefaultAppDomain(pszAssemblyPath,
357: pszClassName, pszStaticMethodName, pszStringArg, &dwLengthRet);
358: if (FAILED(hr))
359: {
360: wprintf(L"Failed to call GetStringLength w/hr 0x%08lx\n", hr);
361: goto Cleanup;
362: }
363: // Print the call result of the static method.
364: wprintf(L"Call %s.%s(\"%s\") => %d\n", pszClassName, pszStaticMethodName,
365: pszStringArg, dwLengthRet);
366: Cleanup:
367: if (pMetaHost)
368: {
369: pMetaHost->Release();
370: pMetaHost = NULL;
371: }
372: if (pRuntimeInfo)
373: {
374: pRuntimeInfo->Release();
375: pRuntimeInfo = NULL;
376: }
377: if (pClrRuntimeHost)
378: {
379: // Please note that after a call to Stop, the CLR cannot be
380: // reinitialized into the same process. This step is usually not
381: // necessary. You can leave the .NET runtime loaded in your process.
382: //wprintf(L"Stop the .NET runtime\n");
383: //pClrRuntimeHost->Stop();
384: pClrRuntimeHost->Release();
385: pClrRuntimeHost = NULL;
386: }
387: return hr;
388: }
Original post:http://blogs.msdn.com/b/msdnforum/archive/2010/07/09/use-clr4-hosting-api-to-invoke-net-assembly-from-native-c.aspx
I realiezed the second function in MFC console, nor the other.
The mechanism of UG/NX Load .net dll
Now we talk about how UG/NX load a .net plugin dll.
As far as we know, NX run without .net framework, i.e., no need setup .net framework on the PC to use NX.
But, why NX can also load .net dll. (Of cource, who use .net plugin should setup .net framework by selves).
As I known, a native C/C++ program to load a .net program could use CLI, COM, and API methods. Others are between processes.
Obviously, NX didn't use CLI nor COM, because of both methods should prepare .net framework on PC.
API is the short of CLR Hodting APIs, some COM components to load .net program.
API is different with .net versions, especially .net4 having in process side by side which let it be reprogram. (The usage of .net API is not the businee of this post and I would post another article latter).
Conclusion, NX load the customer .net dll with ManagedLoader.dll by .net hosing API.
ManagedLoader.dll is a key component.
Let us have a look for it.
From the Fig., we see a interface and a class which is a implementation of the former.
The interface realiezed how to load a .net dll, and run their methods.
We pay attension to Load(string), for it is take charge for loading the customer dll. So, let's look:
1: public string Load(string assemblyName)
2: {
3: string str = null;
4: this.myAssemblyName = assemblyName;
5: Trace.WriteLine(string.Concat(new object[] { "ManagedLoader.Load: ", assemblyName, " ", AppDomain.CurrentDomain }));
6: Trace.WriteLine("AppBase: " + AppDomain.CurrentDomain.BaseDirectory);
7: try
8: {
9: AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.AssemblyResolveHandler);
10: AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(this.AssemblyLoadHandler);
11: this.myAssembly = Assembly.LoadFrom(assemblyName);
12: }
13: catch (Exception exception)
14: {
15: Trace.WriteLine(exception.ToString());
16: str = exception.ToString();
17: }
18: return str;
19: }
20:
21:
Line 9, mainly correspond to loading dll successfully event. There is nothing, only Trace information for debugging.
Line 10, deal with loading the dependent dlls, very importantly, details latter.
Line 11, load customer .net dll. Note, it use LoadForm, not LoadFile, there is differen between them.
In addition, reflection have some variation in .net4, you could look for MSDN.
What are dependent dlls?
Different from native dlls which could be found form the Path directory. .net dll only could put donw in exe path or GAC.
That's the problem. The main process of UG/NX is ugraf.exe, at program files/UGS… . It could not ask put all customer dlls in there, so there must be some place for users.
Except the path of the customer dll, NX allows the path speciafied by the system variable UGII_MANAGED_DLL_PATH to put down.
The Event AssemblyResolve deal with searching these dependent dlls, you can look:http://msdn.microsoft.com/zh-cn/library/system.appdomain.assemblyresolve(v=vs.80).aspx
Now, let's see the implementation of above line 10 function.
1: private Assembly AssemblyResolveHandler(object sender, ResolveEventArgs args)
2: {
3: Trace.WriteLine("Resolve failed: " + args.Name);
4: int index = args.Name.IndexOf(',');
5: string str = args.Name.Substring(0, index);
6: if ((!(str == "NXOpen") && !(str == "NXOpen.Utilities")) && (!(str == "NXOpenUI") && !(str == "NXOpen.UF")))
7: {
8: return null;
9: }
10: str = str + ".dll";
11: string str2 = TranslateVariable("UGII_MANAGED_DLL_PATH");
12: string path = null;
13: if (str2 != null)
14: {
15: foreach (string str4 in str2.Split(new char[] { Path.PathSeparator }))
16: {
17: path = Path.Combine(str4, str);
18: if (File.Exists(path))
19: {
20: break;
21: }
22: path = null;
23: }
24: }
25: if (path == null)
26: {
27: str2 = TranslateVariable("UGII_ROOT_DIR");
28: if (str2 != null)
29: {
30: path = Path.Combine(Path.Combine(str2, "managed"), str);
31: }
32: }
33: Assembly assembly = Assembly.LoadFrom(path);
34: if (assembly == null)
35: {
36: Trace.WriteLine("Failed to load: " + str);
37: return assembly;
38: }
39: Trace.WriteLine("Loaded: : " + assembly.GetName(false).ToString());
40: return assembly;
41: }
4-5, acquire the name of a dependent dll.
6-9, exclude 4 dlls laoding automatically with ManagedLoader.dll.
10-24, search them in the path speciafied by UGII_MANAGED_DLL_PATH.
25-32, search them in the path specialfied by UGII_ROOR_DIR which is the root of UG/NX.
Line 33, load the dependent dll.
34-38, deal with errors.
Ohters are searched form the path of the customer plugin, which is automatically realized by reflection of .net.
OK, that's all.
About .net API usage, we talk about latter.
All rights 白途思(Beck)
2011-03-01
2011-02-10
Application of a UG CAM API: UF_SETUP_generate_program
Application of a UG CAM API: UF_SETUP_generate_program
Beck (BAI Tusi)
Email/MSN:begtostudy@gmail.com
Defined in: uf_setup.h
Overview
This function generates the NC program for all the operations contained in the specified group of the specified setup. It will use the Post (i.e., the Definition File and Event Handler) specified by 'post_name'. It will write the generated NC Program to a file with name 'output_filename'. If 'output_filename' exists the output will be appended to the existing file. If the behavior wanted is to overwrite the exsiting file, then the user has to delete the file before calling this function.
See the Note in the File Description regarding the relationship between the 'post_name' and the CAM resource files.
Environment
Internal and External
History
Originally released in V16.0
Required License(s)
cam_base
int UF_SETUP_generate_program
(
tag_t setup,
tag_t group,
const char * post_name,
const char * output_filename,
UF_SETUP_output_units_t output_units
)
setup | Input | - the setup object to process | |
group | Input | - the program group to process | |
const char * | post_name | Input | - the name (as found in a resource file) of the post instructions to use. This name specifies the Definition File and Event Handler file to use |
const char * | output_filename | Input | - the name of the file to write the output to |
output_units | Input | - the output units for the Post to use |
Before using this function, we must setup something as follows:
Firstly, we should find the folder from the NX setup folder as shown in Figure 1: \UGS\NX 6.0\MACH\resource\postprocessor
And then, copy three post-process files into this folder, as shown in Figure 3:
图 1
Open template_post.dat in Figure 1 by notepad (Figure 2).
Append a line in the file:
begtostudy,${UGII_CAM_POST_DIR}begtostudy.tcl,${UGII_CAM_POST_DIR}begtostudy.def
It specifies the name, tcl file and def file, divided by ‘;’ respectively.
${UGII_CAM_POST_DIR} is the special the folder, of course, you could use a full path name.
图 2
After restarted UG, it would display the configuration in the post-processor dialog (Figure 3).
图 3
At last, we could use the api, UF_SETUP_generate_program, in our program:
UF_CALL(UF_SETUP_generate_program(setup_tag,pro_tag/*Program group*/,”begtostudy”,filename/*output the NC file*/, UF_SETUP_OUTPUT_UNITS_INCH ));
Note: You must ensure you can generate nc file by hand in NX.
End.
2011-01-11
Open APIs and NXOpen Class Library of UG/NX Secondary Development
(I am not native speaker, so sorry for my English. If you find some mistakes, please tell me in the following comments or email to me: begtostudy@gmail.com)
There are two open application interface system, i.e., Open APIs and NXOpen class library. Open, also named UFun, is based on C-Langrage at the start and later supported C++, which is good at geometry functions. After purchased by Siemens (or extended to PLM business), UG/NX have another one, NXOpen class library. It concludes almost the functions of NX software and is entirely object-oriented. However NX didn’t give up the Open APIs, with NXOpen.UF namespace, we can use them by encapsulation technology. Maybe for the short history, NXOpen have less help data than Open. And the same reason, before NX6, there are so many bugs in NXOpen. We give some comparisons in Table 1.
Table 1. Comparisons
Language | Open API/UFun | NXOpen |
C | procedure-oriented good at geometry functions, especially efficiency | Not support |
C++ | (Open C++) Partly object-oriented Partly support NX functions | Almost support NX functions (native code) |
.net/java | NXOpen.UF namespace by encapsulation technology | Almost support NX functions (managed code) |
Following, I would take some examples to illustrate their difference. For .net, I only picked C#, and others, e.g. VB, are the same. Sorry for java, I know little about it.
I. Open C
1. Find each body in the work part
#include <uf_object_types.h>#include <uf_part.h>
#include <uf_obj.h>
#include <uf_modl.h>
#include <string>
#include <sstream>
using std::string;
using std::stringstream;
//code segments as follow
UgSession session( true );
try
{
/* TODO: Add your application code here */
uf_list_p_t lpObj;
UF_MODL_create_list(&lpObj);
tag_t prt = UF_PART_ask_display_part();
tag_t Next_tag=NULL_TAG;
do
{
UF_OBJ_cycle_objs_in_part(prt,UF_solid_type,&Next_tag);
if(Next_tag==NULL_TAG) break;
int t,subtype;
UF_OBJ_ask_type_and_subtype(Next_tag,&t,&subtype);
if(subtype==UF_solid_body_subtype)
UF_MODL_put_list_item(lpObj,Next_tag);
} while(1);
logical is_open;
UF_UI_is_listing_window_open(&is_open);
if(!is_open) UF_UI_open_listing_window();
int sum;
UF_MODL_ask_list_count(lpObj,&sum);
for (int i=0;i<sum;i++)
{
tag_t t;
UF_MODL_ask_list_item(lpObj,i,&t);
stringstream s;
s<<(int)t;
string str;
str = s.str();
UF_UI_write_listing_window(str.c_str());
UF_UI_write_listing_window("\n");
}
// UF_UI_exit_listing_window();
UF_MODL_delete_list(&lpObj);
}
/* Handle errors */
catch ( const UgException &exception )
{
processException( exception );
}
2. Create a block
#include <uf.h>#include <uf_ui.h>
#include <uf_exit.h>
#include <uf_modl.h>
//code segments as follow
/* Initialize the API environment */
if( UF_CALL(UF_initialize()) )
{
/* Failed to initialize */
return;
}
/* TODO: Add your application code here */
double corner[3] ={0,0,0};
char* edge[3] = {"10","5","20"};
tag_t tag;
UF_MODL_create_block(UF_NULLSIGN,NULL_TAG,corner,edge,&tag);
/* Terminate the API environment */
UF_CALL(UF_terminate());
II. Open C++
1. Find each body in the work part
#include <ug_typed.hxx>#include <ug_part.hxx>
#include <ug_body.hxx>
#include <ug_string.hxx>
using std::string;
// code segments as follow
UgSession session( true );
try
{
/* TODO: Add your application code here */
UgPart *pWorkPart = UgSession::getWorkPart();
UgTypedObject *pObj;
ostrstream buffer;
for ( pObj = pWorkPart->iterateFirst ( );
pObj;
pObj = pWorkPart->iterateNext ( pObj ) )
{
std::string name = pObj->getName ( );
UgBody *pBody = dynamic_cast<UgBody*>(pObj);
if (pBody)
{
buffer<<(int)pBody->getTag()<<"\n";
}
}
UgInfoWindow::open();
UgInfoWindow::write(string(buffer.str()));
delete buffer.str();
}
/* Handle errors */
catch ( const UgException &exception )
{
processException( exception );
}
2. serching
#include <ug_body.hxx>#include <ug_iterator.hxx>
#include <ug_string.hxx>
// code segments as follow
UgSession session( true );
try
{
/* TODO: Add your application code here */
ostrstream buffer;
// Construct an iterator for NX face objects
UgIterator < UgBody > pObj;//workpart 可以通过其他方式指定
//UgIterator < UgFace *> curFace;
//// Loop through all faces
//while ( !curFace.isFinished ( ) )
//{
// // Get the name of the current face
// std::string faceName = (*(*curFace))->getName ( );
// curFace.findNext ( );
//}
// Loop through all faces
while ( !pObj.isFinished ( ) )
{
// Get the name of the current face
std::string faceName = (*pObj)->getName ( );
buffer<<(int)(*pObj)->getTag()<<endl;
pObj.findNext ( );
}
UgInfoWindow::open();
UgInfoWindow::write( std::string( buffer.str() ));
delete buffer.str();
}
/* Handle errors */
catch ( const UgException &exception )
{
processException( exception );
}
III. NXOpen C++
1. Create a block
#include <NXOpen/Session.hxx>#include <NXOpen/Part.hxx>
#include <NXOpen/Features_BlockFeatureBuilder.hxx>
#include <NXOpen/Features_Block.hxx>
#include <NXOpen/PartCollection.hxx>
#include <NXOpen/Features_FeatureCollection.hxx>
#include <NXOpen/UI.hxx>
#include <NXOpen/NXMessageBox.hxx>
using namespace NXOpen;
// code segments as follow
NXOpen::Session *theSession = NXOpen::Session::GetSession();
try
{
/* TODO: Add your application code here */
Part* thePart = theSession->Parts()->Work();
NXOpen::Features::Feature* block = NULL;
NXOpen::Features::BlockFeatureBuilder* theBuilder = thePart->Features()->CreateBlockFeatureBuilder(block);
NXOpen::Point3d basePoint(100, 100, 100);
theBuilder->SetOriginAndLengths(basePoint, "100", "200", "300");
// NXOpen.Body theBody = null;
// theBuilder.SetBooleanOperationAndTarget(NXOpen.Features.Feature.BooleanType.Create, theBody);
theBuilder->Commit();
//theBuilder->CommitFeature();
NXOpen::UI::GetUI()->NXMessageBox()->Show("", NXMessageBox::DialogType::DialogTypeInformation, "OK!");
// UI->GetUI()->NXMessageBox.Show("", NXMessageBox.DialogType.Information, "OK!");
}
/* Handle errors */
catch ( const UgException &exception )
{
processException( exception );
}
2. Find each Feature in the work part
#include <NXOpen/Session.hxx>#include <NXOpen/Part.hxx>
#include <NXOpen/Features_BlockFeatureBuilder.hxx>
#include <NXOpen/Features_Block.hxx>
#include <NXOpen/PartCollection.hxx>
#include <NXOpen/Features_FeatureCollection.hxx>
#include <NXOpen/UI.hxx>
#include <NXOpen/NXMessageBox.hxx>
#include <NXOpen/ListingWindow.hxx>
using namespace NXOpen;
// code segments as follow
NXOpen::Session *theSession = NXOpen::Session::GetSession();
try
{
/* TODO: Add your application code here */
Part* thePart = theSession->Parts()->Work();
theSession->ListingWindow()->Open();
NXOpen::Features::FeatureCollection::iterator i;
for (i=thePart->Features()->begin();i!=thePart->Features()->end();i++)
{
theSession->ListingWindow()->WriteLine((*i)->Name()+"--"+(*i)->GetJournalIdentifier());
}
NXOpen::UI::GetUI()->NXMessageBox()->Show("", NXMessageBox::DialogType::DialogTypeInformation, "OK!");
// UI->GetUI()->NXMessageBox.Show("", NXMessageBox.DialogType.Information, "OK!");
}
/* Handle errors */
catch ( const UgException &exception )
{
processException( exception );
}
IV. NXOpen C#
1. Create a block
using NXOpen;using NXOpen.Utilities;
using NXOpen.UF;
using NXOpenUI;
// code segments as follow
Session theSession = Session.GetSession();
try
{
Part thePart = theSession.Parts.Work;
NXOpen.Features.Feature block = null;
NXOpen.Features.BlockFeatureBuilder theBuilder = thePart.Features.CreateBlockFeatureBuilder(block);
NXOpen.Point3d basePoint = new Point3d(100f, 100f, 100f);
theBuilder.SetOriginAndLengths(basePoint, "100", "200", "300");
theBuilder.Commit();
//theBuilder.CommitFeature();
UI.GetUI().NXMessageBox.Show("", NXMessageBox.DialogType.Information, "OK!");
}
catch(NXException ex)
{
UI.GetUI().NXMessageBox.Show("error!", NXMessageBox.DialogType.Error, ex.Message);
}
2. Find each Feature in the work part
using NXOpen;using NXOpen.Utilities;
using NXOpen.UF;
using NXOpenUI;
// code segments as follow
Session theSession = Session.GetSession();
theSession.ListingWindow.Open();
try
{
Part thePart = theSession.Parts.Work;
foreach (NXOpen.Features.Feature c in thePart.Features)
{
//NXOpen.Features.Block t = c as NXOpen.Features.Block;
//if(t!=null)
//{
// theSession.ListingWindow.WriteLine(t.ToString());
//}
theSession.ListingWindow.WriteLine(c.Name+"--"+c.ToString());
}
UI.GetUI().NXMessageBox.Show("", NXMessageBox.DialogType.Information, "OK!");
}
catch (NXException ex)
{
UI.GetUI().NXMessageBox.Show("error!", NXMessageBox.DialogType.Error, ex.Message);
}
V. NXOpen.UF
1. analogy of Case I.1 for Open C
using NXOpen;using NXOpen.Utilities;
using NXOpen.UF;
using NXOpenUI;
NXOpen.UF.UFSession theUFSession = NXOpen.UF.UFSession.GetUFSession();
try
{
double[] corner ={ 0, 0, 0 };
string[] edge = { "10", "5", "20" };
Tag tag;
theUFSession.Modl.CreateBlock1(FeatureSigns.Nullsign, corner, edge, out tag);
}
catch (NXException ex)
{
UI.GetUI().NXMessageBox.Show("error!", NXMessageBox.DialogType.Error, ex.Message);
}
1. analogy of Case I.1 for Open C
using NXOpen;using NXOpen.Utilities;
using NXOpen.UF;
using NXOpenUI;
NXOpen.UF.UFSession theUFSession = NXOpen.UF.UFSession.GetUFSession();
try
{
Tag[] list=null;
Tag thePart = theUFSession.Part.AskDisplayPart();
theUFSession.Modl.CreateList(out list);
Tag Next_tag=Tag.Null;
do
{
theUFSession.Obj.CycleObjsInPart(thePart,70/* UF_solid_type*/,ref Next_tag);
if (Next_tag == Tag.Null) break;
int t, subType;
theUFSession.Obj.AskTypeAndSubtype(Next_tag,out t, out subType);
if (subType == 0/*UF_solid_body_subtype*/)
theUFSession.Modl.PutListItem(list, Next_tag);
} while (true);
bool isOpen;
theUFSession.Ui.IsListingWindowOpen(out isOpen);
if (!isOpen) theUFSession.Ui.OpenListingWindow();
int sum;
theUFSession.Modl.AskListCount(list,out sum);
for (int i = 0; i < sum;i++ )
{
Tag t;
theUFSession.Modl.AskListItem(list, i, out t);
theUFSession.Ui.WriteListingWindow(t.ToString());
}
/*Treat all the arguments with the"Output to be freed " annotation as an output parameter.
* The system takes care of freeing memory.*/
}
catch (NXException ex)
{
UI.GetUI().NXMessageBox.Show("error!", NXMessageBox.DialogType.Error, ex.Message);
}