Created
February 10, 2011 06:41
-
-
Save juntalis/820052 to your computer and use it in GitHub Desktop.
Ran into some issues building a Titanium SDK Desktop app in their IDE, so I ended up writing a quick MsBuild task to automate it. Didn't want to run the python executable, so I instead opted to dynamically load the DLL.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.IO; | |
using System.Runtime.InteropServices; | |
using Microsoft.Build.Framework; | |
using Microsoft.Build.Utilities; | |
namespace MsBuild.TiDesktop | |
{ | |
// TODO: Turn this into a property for the task. | |
public const string sdkVersion = "1.1.0"; | |
/// <summary> | |
/// | |
/// </summary> | |
public class BuildTask : Task | |
{ | |
/// <summary> | |
/// To load the dll - <paramref name="dllFilePath"/> doesn't have to be | |
/// constant - so I can read path from registry | |
/// </summary> | |
/// <param name="dllFilePath">file path with file name</param> | |
/// <param name="hFile">use <see cref="IntPtr.Zero"/></param> | |
/// <param name="dwFlags">What will happened during loading dll | |
/// <para>LOAD_LIBRARY_AS_DATAFILE</para> | |
/// <para>DONT_RESOLVE_DLL_REFERENCES</para> | |
/// <para>LOAD_WITH_ALTERED_SEARCH_PATH</para> | |
/// <para>LOAD_IGNORE_CODE_AUTHZ_LEVEL</para> | |
/// </param> | |
/// <returns>Pointer to loaded Dll</returns> | |
[DllImport("KERNEL32.DLL")] | |
private static extern IntPtr LoadLibraryEx(string dllFilePath, IntPtr hFile, uint dwFlags); | |
/// <summary> | |
/// To unload library | |
/// </summary> | |
/// <param name="dllPointer">Pointer to Dll witch was returned from <see cref="LoadLibraryEx"/></param> | |
/// <returns>If unloaded library was correct then <see langword="true"/>, else <see langword="false"/></returns> | |
[DllImport("KERNEL32.DLL")] | |
public static extern bool FreeLibrary(IntPtr dllPointer); | |
/// <summary> | |
/// To get function pointer from loaded dll | |
/// </summary> | |
/// <param name="dllPointer">Pointer to Dll witch was returned from <see cref="LoadLibraryEx"/></param> | |
/// <param name="functionName">Function name with you want to call</param> | |
/// <returns>Pointer to function</returns> | |
[DllImport("KERNEL32.DLL")] | |
public static extern IntPtr GetProcAddress(IntPtr dllPointer, string functionName); | |
/// <summary> | |
/// This will to load concrete dll file | |
/// </summary> | |
/// <param name="dllFilePath">Dll file path</param> | |
/// <returns>Pointer to loaded dll</returns> | |
/// <exception cref="ApplicationException"> | |
/// when loading dll will failure | |
/// </exception> | |
public static IntPtr LoadWin32Library(string dllFilePath) | |
{ | |
IntPtr moduleHandle = LoadLibraryEx(dllFilePath, IntPtr.Zero, 0x00000008); | |
if (moduleHandle == IntPtr.Zero) { | |
// I'm gettin last dll error | |
int errorCode = Marshal.GetLastWin32Error(); | |
throw new ApplicationException( | |
string.Format("There was an error during dll loading : {0}, error - {1}", dllFilePath, errorCode) | |
); | |
} | |
return moduleHandle; | |
} | |
// ************************************************************************************************************************** | |
// That is all, now how to use this functions | |
// ************************************************************************************************************************** | |
/// <summary> | |
/// | |
/// </summary> | |
/// <exception cref="ApplicationException"></exception> | |
public static void InitializeMyDll(string pathDLL, out IntPtr myDll) | |
{ | |
try { | |
myDll = LoadWin32Library(pathDLL); | |
} | |
catch (ApplicationException) { | |
myDll = IntPtr.Zero; | |
throw; | |
} | |
} | |
// The last thing is to create delegate to calling function | |
// delegate must to have the same parameter then calling function (from dll) | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="argc"></param> | |
/// <param name="argv"></param> | |
[UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Unicode)] | |
public delegate int Py_Main(int argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeConst = 9)] string[] argv); | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="pyPath"></param> | |
/// <param name="argc"></param> | |
/// <param name="argv"></param> | |
/// <returns></returns> | |
public static int PyMain(string pyPath, int argc, string[] argv) | |
{ | |
IntPtr myDll; | |
InitializeMyDll(pyPath, out myDll); | |
if(myDll == IntPtr.Zero) { | |
return 3; | |
} | |
IntPtr pProc = GetProcAddress(myDll, @"Py_Main"); | |
Delegate dPyMain = null; | |
try { | |
dPyMain = Marshal.GetDelegateForFunctionPointer(pProc, typeof(Py_Main)); | |
} catch (ArgumentException e) { | |
Console.WriteLine(e.ParamName); | |
Console.WriteLine(e.Message); | |
Console.WriteLine(e); | |
} | |
if (dPyMain != null) { | |
int _result = (int)(dPyMain.DynamicInvoke(argc, argv)); | |
FreeLibrary(myDll); | |
return _result; | |
} | |
return 3; | |
} | |
// Now if you want to call dll function from program code use this | |
// for ex. you want to call c++ function RETURN_TYPE CallingFunctionNameFromCallingDllFile(int nID, LPSTR lpstrName); | |
/// <summary> | |
/// Executes a task. | |
/// </summary> | |
/// <returns> | |
/// <see langword="true"/> if the task executed successfully; otherwise, <see langword="false"/>. | |
/// </returns> | |
public override bool Execute() | |
{ | |
//const string sdkVersion = "1.1.0"; | |
Log.LogMessage(MessageImportance.High, "Locating path to Titanium SDK."); | |
string pathProgData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); // Environment.GetEnvironmentVariable(@"ProgramData"); | |
if(string.IsNullOrEmpty(pathProgData)) { | |
Log.LogErrorFromException(new DirectoryNotFoundException("ProgramData couldn't be resolved.")); | |
return false; | |
} | |
Log.LogMessage(MessageImportance.Low, String.Format("Checking if Titanium folder exists in {0}..", pathProgData)); | |
pathProgData = Path.Combine(pathProgData, "Titanium"); | |
if ((!Directory.Exists(pathProgData)) || (Directory.GetFileSystemEntries(pathProgData).Length < 1)) { | |
Log.LogErrorFromException(new DirectoryNotFoundException("Directory \"" + pathProgData + "\" does not exist.")); | |
return false; | |
} | |
// Resolve the paths. | |
Log.LogMessage(MessageImportance.Normal, "Verifying existence of Titanium's python distribution and build script."); | |
string pathPython = String.Format("{0}{1}modules{1}win32{1}python{1}{2}", pathProgData, Path.DirectorySeparatorChar, sdkVersion); | |
string pathBuildSDK = String.Format("{0}{1}sdk{1}win32{1}{2}", pathProgData, Path.DirectorySeparatorChar, sdkVersion); | |
string pathBuildScript = String.Format("{0}{1}tibuild.py", pathBuildSDK, Path.DirectorySeparatorChar); | |
string pathPyDll = Path.Combine(pathPython, "python25.dll"); | |
string pathPyLib = String.Format("{0}{1}{0}{2}Lib{1}{0}{2}tcl{1}{0}{2}DLLs", pathPython, Path.PathSeparator, Path.DirectorySeparatorChar); | |
string pathPyBin = String.Format("{0}{1}{0}{2}Lib{2}Tools{2}Scripts", pathPython, Path.PathSeparator, Path.DirectorySeparatorChar); | |
if(!File.Exists(pathBuildScript)||!File.Exists(pathPyDll)) { | |
Log.LogErrorFromException(new FileNotFoundException("Could not find tibuild.py. (or could not find interpreter to run it)")); | |
Log.LogMessage(MessageImportance.High, "Verify they exist as \n{0}\nand\n{1}", pathPyDll, pathBuildScript); | |
return false; | |
} | |
Environment.SetEnvironmentVariable("PATH", pathPyBin, EnvironmentVariableTarget.Process); | |
Environment.SetEnvironmentVariable("PYTHONPATH", pathPyLib, EnvironmentVariableTarget.Process); | |
Environment.SetEnvironmentVariable("PYTHONDEBUG", "1", EnvironmentVariableTarget.Process); | |
// Temporary | |
string dest = Destination.ItemSpec; | |
string src = Source.ItemSpec; | |
String[] args = { | |
"python", | |
pathBuildScript, | |
"-d", dest, | |
"-s", String.Format("{0}{1}", pathProgData, Path.DirectorySeparatorChar), | |
"-a", pathBuildSDK, | |
src | |
}; | |
if (PyMain(pathPyDll, 9, args) != 0) { | |
_results = "Your Titanium build has failed."; | |
Log.LogMessage(MessageImportance.High, _results); | |
return false; | |
} | |
_results = "Your Titanium build has succeeded!"; | |
return true; | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
[Required] | |
public ITaskItem Source | |
{ | |
get { return _sourceFolder; } | |
set { _sourceFolder = value ?? _sourceFolder; } | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
[Required] | |
public ITaskItem Destination | |
{ | |
get { return _destFolder; } | |
set { _destFolder = value ?? _destFolder; } | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
[Output] | |
public string Results | |
{ | |
get { return !string.IsNullOrEmpty(_results) ? _results : "Failed"; } | |
} | |
private string _results; | |
private ITaskItem _sourceFolder; | |
private ITaskItem _destFolder; | |
/// <summary> | |
/// | |
/// </summary> | |
public BuildTask() | |
{ | |
_results = String.Empty; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment