-- -- Useful functions for things that use DotNet -- -- Neal D Corbett -- (Rockstar Leeds) -- 11/03/2011 -- -- Returns an instance of a DotNet assembly stored in "globVal", compiling it from "source" if that's invalid: fn RsGetCompiledAssembly name source refList &globVal forceRecompile:false = ( if forceRecompile or ((not isKindOf globVal dotNetObject) or (((globVal.GetType()).ToString()) != "System.Reflection.RuntimeAssembly")) do ( local csharpProvider = dotnetobject "Microsoft.CSharp.CSharpCodeProvider" local compilerParams = dotnetobject "System.CodeDom.Compiler.CompilerParameters" -- Add referenced assemblies that the code snippet is 'using' for item in refList do ( compilerParams.ReferencedAssemblies.Add item ) compilerParams.GenerateInMemory = true local compilerResults = csharpProvider.CompileAssemblyFromSource compilerParams #(source) local errors = compilerResults.Errors if (errors.Count > 0 ) then ( local errs = stringstream "" for i = 0 to (errors.Count - 1) do ( local err = compilerResults.Errors.Item[i] format "Error:% Line:% Column:% %\n" err.ErrorNumber err.Line err.Column err.ErrorText to:errs ) MessageBox (errs as string) title: "Errors encountered while compiling C# code" format "%\n" errs globVal = undefined ) else ( globVal = compilerResults.CompiledAssembly ) ) if (globVal == undefined) then (return undefined) else (globVal.CreateInstance name) ) ------------------------------------------------------------------------------------------------------------ -- RS_ListViewItemComparer -- Generates and supplies an assembly for sorting a ListView by its columns -- (originally found here: http://forums.cgsociety.org/archive/index.php/t-551473-p-2.html) ------------------------------------------------------------------------------------------------------------ global gRsListViewItemComparerAssembly = undefined fn RS_ListViewItemComparer forceRecompile:false = ( local source = " using System; using System.Windows.Forms; using System.Collections; class gpgxListViewItemComparer : IComparer { public int column; public bool reversed; public gpgxListViewItemComparer() { column = 0; reversed = false; } public gpgxListViewItemComparer(int col, bool rev) { column = col; reversed = rev; } public int Compare(object x, object y) { int ret = 0; ListViewItem lvx = (ListViewItem)x; ListViewItem lvy = (ListViewItem)y; if (column >= 0 && column < lvx.SubItems.Count && column < lvy.SubItems.Count) ret = String.Compare(lvx.SubItems[column].Text, lvy.SubItems[column].Text); return reversed ? -ret : ret; } } " RsGetCompiledAssembly "gpgxListViewItemComparer" source #("System.dll", "System.Windows.Forms.dll") &gRsListViewItemComparerAssembly forceRecompile:forceRecompile ) ------------------------------------------------------------------------------------------------------------ -- RS_NoFocusDotNetForm -- Generates and supplies a DotNet Form that appears without taking focus automatically ------------------------------------------------------------------------------------------------------------ global gRsNoFocusFormAssembly = undefined fn RS_NoFocusDotNetForm forceRecompile:false = ( local source = " using System; using System.Windows.Forms; public class NoFocusForm : Form { protected override bool ShowWithoutActivation { get { return true; } } } " RsGetCompiledAssembly "NoFocusForm" source #("System.dll", "System.Windows.Forms.dll") &gRsNoFocusFormAssembly forceRecompile:forceRecompile ) ------------------------------------------------------------------------------------------------------------ -- RS_disableProcessWindowsGhosting -- Turns off window-ghosting for the current session of Max ------------------------------------------------------------------------------------------------------------ --copied from cgtalk --http://forums.cgsociety.org/showthread.php?f=98&t=958314&page=2&pp=15 --this turns off window-ghosting for that session of Max global gRsDisableGhostingAssembly = undefined fn RS_disableProcessWindowsGhosting forceRecompile:false = ( local source = " using System.Runtime.InteropServices; public class DisableWindowsGhosting { [DllImport(\"user32.dll\")] public static extern bool DisableProcessWindowsGhosting(); } " local assembly = RsGetCompiledAssembly "DisableWindowsGhosting" source #("System.dll") &gRsDisableGhostingAssembly forceRecompile:forceRecompile if (assembly != undefined) do ( assembly.DisableProcessWindowsGhosting() ) ) ------------------------------------------------------------------------------------------------------------ -- RS_CustomDataGrid -- Generates a double-buffered DataGridView object-type, -- with fake cells drawn in empty area at bottom of list ------------------------------------------------------------------------------------------------------------ global gRsCustomDataGridRecompile = undefined global gRsCustomDataGridAssembly = undefined fn RS_CustomDataGrid forceRecompile:false = ( local source = " using System; using System.Windows.Forms; using System.Drawing; class RsCustomDataGridView: DataGridView { int hScrollNow = 0; // Allows numbers and non-numbers to be sorted together: private void customSortCompare(object sender, DataGridViewSortCompareEventArgs e) { if ((e.CellValue1 == null) && (e.CellValue2 == null)) { e.SortResult = 0; } else if (e.CellValue1 == null) { e.SortResult = -1; } else if (e.CellValue2 == null) { e.SortResult = 1; } else { string aString = String.Empty, bString = String.Empty; bool aIsString = false, bIsString = false; if ((e.CellValue1.GetType()) == typeof(String)) { aString = e.CellValue1.ToString(); aIsString = true; } if ((e.CellValue2.GetType()) == typeof(String)) { bString = e.CellValue2.ToString(); bIsString = true; } if (!aIsString && !bIsString) { e.SortResult = (System.Convert.ToDouble(e.CellValue1)).CompareTo(System.Convert.ToDouble(e.CellValue2)); } else { if (!aIsString) { aString = String.Empty; } if (!bIsString) { bString = String.Empty; } e.SortResult = aString.CompareTo(bString); } } e.Handled = true; } public RsCustomDataGridView() { DoubleBuffered = true; RowHeadersVisible = false; RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.DisableResizing; ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.DisableResizing; SortCompare += customSortCompare; } protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); DataGridView.HitTestInfo hitTestInfo = this.HitTest(e.X, e.Y); if (hitTestInfo.Type == DataGridViewHitTestType.ColumnHeader) { this.DoubleBuffered = false; } } protected override void OnMouseUp(MouseEventArgs e) { base.OnMouseUp(e); if (!this.DoubleBuffered) { this.DoubleBuffered = true; } } // Draw empty rows at bottom of list, instead of grey box: protected override void OnPaint(PaintEventArgs e) { base.OnPaint(e); // Invalidate view if it's been scrolled horizontally: foreach (Control control in this.Controls) { if (control is HScrollBar) { HScrollBar hScroll = (HScrollBar)control; if (hScroll.Visible) { if (hScrollNow != hScroll.Value) { hScrollNow = hScroll.Value; this.Invalidate(); } } } } int rowHeight = this.RowTemplate.Height; int columnHeaderHeight = ColumnHeadersVisible ? ColumnHeadersHeight : 0; int h = columnHeaderHeight + this.Rows.GetRowsHeight(DataGridViewElementStates.None) ; int imgWidth = this.Width - 2; Rectangle rFrame = new Rectangle(0, 0, imgWidth, rowHeight); Rectangle rFill = new Rectangle(1, 1, imgWidth - 2, rowHeight); Pen pen = new Pen(this.GridColor, 1); Bitmap rowImg = new Bitmap(imgWidth, rowHeight); Graphics g = Graphics.FromImage(rowImg); g.DrawRectangle(pen, rFrame); g.FillRectangle(new SolidBrush(this.DefaultCellStyle.BackColor), rFill); Bitmap rowImgAAlternative = rowImg.Clone() as Bitmap; Graphics g2 = Graphics.FromImage(rowImgAAlternative); rFill.X -= 1; g2.FillRectangle(new SolidBrush(this.AlternatingRowsDefaultCellStyle.BackColor), rFill); DataGridViewColumnCollection cols = this.Columns; DataGridViewColumn thisCol = cols.GetFirstColumn(DataGridViewElementStates.None); int w = -hScrollNow; for (int j = 0; j < this.ColumnCount; j++) { g.DrawLine(pen, new Point(w, 0), new Point(w, rowHeight)); g2.DrawLine(pen, new Point(w, 0), new Point(w, rowHeight)); if (j != 0) { thisCol = cols.GetNextColumn(thisCol,DataGridViewElementStates.Visible,DataGridViewElementStates.None); } w += thisCol.Width; } int loop = (this.Height - h) / rowHeight; for (int j = 0; j < loop + 1; j++) { int index = this.RowCount + j; if (index % 2 == 0) { e.Graphics.DrawImage(rowImg, 1, h + j * rowHeight); } else { e.Graphics.DrawImage(rowImgAAlternative, 1, h + j * rowHeight); } } // Redraw outer black line: Pen outerPen = new Pen(this.ForeColor, 1); Rectangle outerBox = this.DisplayRectangle; outerBox.Width -= 1; outerBox.Height -= 1; e.Graphics.DrawRectangle(outerPen, outerBox); } } " -- Force recompile if class is undefined for no particular reason: if (dotNetClass "RsCustomDataGridView") == undefined do ( forceRecompile = true ) -- STOOPID WORK-AROUND, PART 1 -- -- For some reason, this custom control works only once after first compiling, but works an unlimited number of times after recompiling: case gRsCustomDataGridRecompile of ( undefined: ( gRsCustomDataGridRecompile = true ) true: ( gRsCustomDataGridRecompile = false forceRecompile = true ) ) RsGetCompiledAssembly "RsCustomDataGridView" source #("System.dll", "System.Windows.Forms.dll", "System.Drawing.dll") &gRsCustomDataGridAssembly forceRecompile:forceRecompile -- STOOPID WORK-AROUND, PART 2 -- -- For the first run of this function, generate a control with this assembly and then re-run the function: if gRsCustomDataGridRecompile do ( rollout tempDataGridRoll "" ( dotNetControl tempCtrl "RsCustomDataGridView" on tempDataGridRoll open do ( destroyDialog tempDataGridRoll ) ) createDialog tempDataGridRoll pos:[-100,-100] RS_CustomDataGrid() ) ) ------------------------------------------------------------------------------------------------------------ -- RS_CustomDataGrid -- Generates a double-buffered DataGridView object-type, -- with fake cells drawn in empty area at bottom of list ------------------------------------------------------------------------------------------------------------ global gRsGroupBoxAssembly = undefined fn RsGroupBox forceRecompile:false = ( local source = " using System; using System.Windows.Forms; using System.Drawing; class RsGroupBox: Panel { string _Title = \"\"; int _BoxPadding = 3; SizeF StringSize; public Color BoxColor; // Set client-size to fit inside box, under title: void SetClientSize() { int InPad = 2; int PanelPad = (_BoxPadding + InPad); int TopPad = PanelPad; if (_Title != \"\") { Graphics g = CreateGraphics(); StringSize = g.MeasureString(Title, Font); TopPad = (int)(StringSize.Height) + InPad; } Padding = new Padding (PanelPad, TopPad, PanelPad, PanelPad); } // Default box-colour is transparent version of forecolor: void SetBoxColorFromFore() { BoxColor = Color.FromArgb (64,ForeColor.R,ForeColor.G,ForeColor.B); } protected override void OnForeColorChanged(EventArgs e) { SetBoxColorFromFore(); Invalidate(); } // Init: public RsGroupBox() { DoubleBuffered = true; ResizeRedraw = true; SetBoxColorFromFore(); SetClientSize(); } // Auto-updates clientsize whenever title/boxpadding are changed: public int BoxPadding { get { return _BoxPadding; } set { _BoxPadding = value; SetClientSize(); } } public string Title { get { return _Title; } set { _Title = value; SetClientSize(); } } // Draw box and label on control-paint: protected override void OnPaint(PaintEventArgs e) { Graphics g = e.Graphics; // Draw box-line: Point BoxPos = new Point (BoxPadding, 0); if (StringSize.Height == 0) { BoxPos.Y = BoxPadding; } else { BoxPos.Y = (int)(StringSize.Height / 2); } // Draw box-lines: Rectangle BoxRect = new Rectangle (BoxPos.X, BoxPos.Y, (Width - (BoxPadding * 2) - 1), (Height - BoxPos.Y - BoxPadding - 1)); Pen BoxPen = new Pen(BoxColor, 1); g.DrawRectangle(BoxPen, BoxRect); // Draw text: if (Title != \"\") { Point StringPos = new Point ((BoxPadding + 3), 0); Rectangle RectTextBack = new Rectangle (StringPos.X, StringPos.Y, (int)StringSize.Width, (int)StringSize.Height); g.FillRectangle (new SolidBrush(BackColor), RectTextBack); g.DrawString (Title, Font, (new SolidBrush(ForeColor)), StringPos.X, StringPos.Y); } } } " -- Force recompile if class is undefined for no particular reason: if (dotNetClass "RsGroupBox") == undefined do ( forceRecompile = true ) RsGetCompiledAssembly "RsGroupBox" source #("System.dll", "System.Windows.Forms.dll", "System.Drawing.dll") &gRsGroupBox forceRecompile:forceRecompile ) ------------------------------------------------------------------------------------------------------------ -- RS_intTo4Bytes -- Outputs a large integer into an array of 4 byte-values ------------------------------------------------------------------------------------------------------------ fn RS_intTo4Bytes num = ( local byte1 = num as integer local byteSize = 256 local mByteSize = byteSize * byteSize local gByteSize = mByteSize * byteSize local byte4 = byte1 / gByteSize byte1 -= (byte4 * gByteSize) local byte3 = byte1 / mByteSize byte1 -= (byte3 * mByteSize) local byte2 = byte1 / byteSize byte1 -= (byte2 * byteSize) return #(byte1, byte2, byte3, byte4) ) ------------------------------------------------------------------------------------------------------------ -- RS_convertBitmapToBytes -- Converts a Max bitmap into a byte-array in BMP format, for use with dotNet objects ------------------------------------------------------------------------------------------------------------ fn RS_convertBitmapToBytes imageBmp = ( local imgWidth = imageBmp.width local rowSize = imgWidth * 3 local rowPadding = for n = 1 to (mod rowSize 4) collect 0 local pixelData = #() local getRow for rowNum = (imageBmp.height - 1) to 0 by -1 do ( getRow = getPixels imageBmp [0,rowNum] imgWidth for pixel in getRow do ( join pixelData #(pixel.r, pixel.g, pixel.b) ) join pixelData rowPadding ) local headerSize = 54 local bmpSize = (pixelData.count + headerSize) local bmpArray = #(66,77) join bmpArray (RS_intTo4Bytes bmpSize) join bmpArray #(0,0,0,0, headerSize,0,0,0, 40,0,0,0) join bmpArray (RS_intTo4Bytes imageBmp.width) join bmpArray (RS_intTo4Bytes imageBmp.height) join bmpArray #(1,0, 24,0, 0,0,0,0) join bmpArray (RS_intTo4Bytes pixelData.count) join bmpArray #(32,46,0,0, 32,46,0,0, 0,0,0,0, 0,0,0,0) join bmpArray pixelData return bmpArray ) ------------------------------------------------------------------------------------------------------------ -- RS_bitmapToImage -- Converts a Max bitmap into a dotNet image ------------------------------------------------------------------------------------------------------------ fn RS_bitmapToImage inBmp = ( local bmpArray = RS_convertBitmapToBytes inBmp local memstream = dotnetobject "System.IO.MemoryStream" bmpArray outImg = (dotnetclass "system.drawing.image").fromstream memstream memstream.close() return outImg ) ------------------------------------------------------------------------------------------------------------ -- Returns Max's HWND in a form compatible with dotNet Form.show function: ------------------------------------------------------------------------------------------------------------ fn RS_dotNetMaxHWND = ( --Get the max handle pointer. local maxHandlePointer=(Windows.GetMAXHWND()) --Convert the HWND handle of Max to a dotNet system pointer local sysPointer = DotNetObject "System.IntPtr" maxHandlePointer --Create a dotNet wrapper containing the maxHWND local maxHwnd = DotNetObject "MaxCustomControls.Win32HandleWrapper" sysPointer return maxHwnd ) ------------------------------------------------------------------------------------------------------------ -- Loads a dot net assembly, can be passed in a pattern of names to load. Returns the dot net object of the -- last assembly to be loaded. ------------------------------------------------------------------------------------------------------------ fn RS_dotNetLoadAssembly assemblyNamePattern = ( local assembly for i = 1 to pluginPaths.count() do ( local pluginPath = pluginPaths.get i local dotNetAssemblies = getFiles (pluginPath + assemblyNamePattern) for filename in dotNetAssemblies do ( try ( format " %\n" (filenameFromPath filename) assembly = dotnet.loadAssembly filename ) catch ( print (getCurrentException()) Messagebox ("Failed to load assembly: " + filename + "\n\n" + (getCurrentException())) title:"Assembly Load Failure" return false ) ) ) assembly ) ------------------------------------------------------------------------------------------------------------ -- Loads common dot net assemblies at startup ------------------------------------------------------------------------------------------------------------ fn RS_dotNetLoadStartupAssemblies = ( -- Load common dotNet assemblies: format "Loading dotnet assemblies: \n" local assemblyNames = #( "RSG.*.dll", "p4api.dll", "System.Windows.Controls.DataVisualization.Toolkit.dll", "Ionic.Zip.Partial.dll", "Ionic.Zip.dll", "Ionic.Zlib.dll", "WPFToolkit.dll" ) for namePattern in assemblyNames do ( RS_dotNetLoadAssembly namePattern ) ) ------------------------------------------------------------------------------------------------------------ -- Print the properties/methods/events for a Dotnet-object: ------------------------------------------------------------------------------------------------------------ fn RsShowDotNetProps obj = ( if (isKindOf obj dotNetObject) or (isKindOf obj dotNetClass) or (isKindOf obj dotNetControl) then ( --clearListener() format "Properties:\n" showProperties obj format "\nMethods:\n" showMethods obj format "\nEvents:\n" showEvents obj ) else ( format "% is not a dotNetObject or dotNetClass\n" (obj as string) ) ) /* USEFUL DOTNET SNIPPETS: To print to Listener from a memory-compiled assembly, include this assembly: "ManagedServices.dll" Use this to print: ManagedServices.MaxscriptSDK.ExecuteMaxscriptCommand(\"format \\\"Value: \" + Value + \"\\n\\\"\"); */