Skip to content

Instantly share code, notes, and snippets.

@juntalis
Created February 10, 2011 06:41
Show Gist options
  • Save juntalis/820052 to your computer and use it in GitHub Desktop.
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.
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