Attaching an ESE database without a clean shutdown - extensible-storage-engine

I have an ESE database ("Vulcan.edb") that I've copied from an in-use application. Naturally, this means the database is in a "dirty shutdown" state. Fortunately, I do have the log file of the database ("V01.log"), according to every similar question I can find, I should be able to attach the database and have the engine recover it. Here is the code I'm using to attach the database, using the managed ESE API (an unmanaged C++ port should be trivial):
public void ReadFromDatabase(string fileName, string logFileBaseName)
{
string databaseDirectory = Path.GetDirectoryName(fileName);
int pageSize;
Api.JetGetDatabaseFileInfo(fileName, out pageSize, JET_DbInfo.PageSize);
Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.DatabasePageSize, pageSize, null);
Api.JetSetSystemParameter(JET_INSTANCE.Nil, JET_SESID.Nil, JET_param.Recovery, 0, "Off");
JET_INSTANCE jetInstance;
string jetInstanceName = Guid.NewGuid().ToString("N");
Api.JetCreateInstance(out jetInstance, jetInstanceName);
Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.BaseName, 0, logFileBaseName);
Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.LogFilePath, 0, databaseDirectory);
Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.TempPath, 0, databaseDirectory);
Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, JET_param.SystemPath, 0, databaseDirectory);
Api.JetSetSystemParameter(jetInstance, JET_SESID.Nil, Microsoft.Isam.Esent.Interop.Server2003.Server2003Param.AlternateDatabaseRecoveryPath, 0, databaseDirectory);
try
{
Api.JetInit(ref jetInstance);
JET_SESID sessionId;
Api.JetBeginSession(jetInstance, out sessionId, "admin", "");
try
{
Api.JetAttachDatabase(sessionId, fileName, AttachDatabaseGrbit.ReadOnly);
JET_DBID databaseId;
Api.JetOpenDatabase(sessionId, fileName, "", out databaseId, OpenDatabaseGrbit.ReadOnly);
// Do other stuff here...
Api.JetCloseDatabase(sessionId, databaseId, CloseDatabaseGrbit.None);
}
finally
{
Api.JetEndSession(sessionId, EndSessionGrbit.None);
}
}
finally
{
Api.JetTerm(jetInstance);
}
}
Indeed, if I copy both the database file and the log file to <directory of database> and run
edbntutl.exe /r V01 /l <directory of database> /s <directory of database> /d <directory of database>,
the database is rendered usable and attachable in my code. However, if I don't take the step of manually recovering the database, my code fails calling JetAttachDatabase with an error of JET_errDatabaseDirtyShutdown.
So why doesn't my code automatically recover the database? Either edbntutl is doing something undocumented, or I'm missing something in my configuration of the session to allow the engine to recover the database. While the former isn't out of the question, it would be incredibly unfortunate if that were the case, and I'm inclined to believe I'm just missing something instead.
Note: Answers which amount to "run esentutil before running your code" will not be accepted as correct without evidence that the utility is doing something not available in the public API.

You need to do a bunch of things to robustly handle loading of esent dbs.
Here is the relevant code that you need in order to handle dirty shutdown, from the RavenDB codebase:
https://github.com/ravendb/ravendb/blob/552208aed679f4a3936a57f9a7506568657e291e/Raven.Database/Storage/Esent/TransactionalStorage.cs#L721-L736

Related

What does mono-io-layer-error (2) mean, starting a process in Unity?

Context is Unity, trying to start a new external process. Code looks like this:
var startinfo = new ProcessStartInfo {
FileName = RootPath + Executable,
WorkingDirectory = RootPath,
UseShellExecute = true,
WindowStyle = Visible ? ProcessWindowStyle.Normal : ProcessWindowStyle.Hidden,
};
try {
_process = Process.Start(startinfo);
return true;
} catch (Exception ex) {
_global.Fail($"Unable to start engine {startinfo.FileName}.\n{ex.ToString()}");
return false;
}
This works just fine in the Editor, but Build&Run fails with the following error:
System.ComponentModel.Win32Exception (0x80004005): mono-io-layer-error (2)
at System.Diagnostics.Process.StartWithShellExecuteEx (System.Diagnostics.ProcessStartInfo startInfo) [0x00102] in <3df7f9ca50404bbc8bd4e7b954e70293>:0
at System.Diagnostics.Process.Start () [0x00032] in <3df7f9ca50404bbc8bd4e7b954e70293>:0
at (wrapper remoting-invoke-with-check) System.Diagnostics.Process.Start()
at System.Diagnostics.Process.Start (System.Diagnostics.ProcessStartInfo startInfo) [0x0001b] in <3df7f9ca50404bbc8bd4e7b954e70293>:0
at ServerManager.StartServer () [0x00049] in <5143145dfb5b4093ac2347595a160b5c>:0
What does it mean? I can find nothing like it on the Web.
My guess is that the feature is perhaps not supported in the built version, but I have no way of finding out for sure or where to look for alternative approaches. At the end of the day all I really need is a way to launch an external process from a built Unity project.
This is a rather peculiar error, it might be a Unity bug. I've noticed that this error occurs when you usually try to concatenate or combine paths in the "FileName". The methods used for finding or combining paths dynamically are seem to be causing this problem. In my case, I was trying to find the path like this:
FileName = Directory.GetCurrentDirectory() + #"\SBERT\dist\SBERT\SBERT.exe",
However, I have found a workaround to this problem by assigning a fully hard-coded path in the "FileName" like:
FileName = #"C:\SBERT\dist\SBERT\SBERT.exe",
Hope this helps for someone, as there is no information about this error on the internet.

How can I replicate New-SmbGlobalMapping in C# code?

I am writing a service which controls docker containers. I want to have the mounted volume as an Azure share, and thus need to use the SMB Global Mapping. If I use the usual WNetAddConnection2A then I can mount the share just fine in my code, but the containers cannot see it as it is not "global". I can't find source for the PowerShell New-SmbGlobalMapping command (is there a way to see it?) and I can't find a suitable API to call. I hope someone knows the magic incantation I can put in my .NET code.
I can't find source for the PowerShell New-SmbGlobalMapping command
(is there a way to see it?) and I can't find a suitable API to call. I
hope someone knows the magic incantation I can put in my .NET code.
PowerShell uses WMI
In your case, it calls
Create method of the MSFT_SmbMapping class (MSFT_SmbGlobalMapping exactly)
You can use WMI Code Creator to generate/test C# code
EDIT : Test with PowerShell.Create
Test as Admin ("requireAdministrator" in manifest) on Windows 10
Test code (C#, VS 2015) =>
// PowerShell calls CredUIPromptForCredentialsW to display the User/Password dialog (you can call it with P/Invoke if needed)
string sUser = "user#provider.com";
string sPassword = "myPassword";
System.Net.NetworkCredential networkCredential = new System.Net.NetworkCredential(sUser, sPassword, null);
System.Security.SecureString securePassword = new System.Security.SecureString();
foreach (var c in networkCredential.Password)
securePassword.AppendChar(c);
// Add reference to :
// C:\Program Files (x86)\Reference Assemblies\Microsoft\WindowsPowerShell\3.0\System.Management.Automation.dll
// Add :
// using System.Management.Automation;
PSCredential psCredential = new PSCredential(networkCredential.UserName, securePassword);
// Error handling must be improved : if I pass an invalid syntax for "RemotePath" or not launched as Admin,
// nothing happens (no error, no result) (on Windows 10)
string sLocalPath = "Q:";
string sRemotePath = "\\\\DESKTOP-EOPIFM5\\Windows 7";
using (var ps = PowerShell.Create())
{
ps.AddCommand("New-SmbGlobalMapping");
ps.AddParameter("LocalPath", sLocalPath);
ps.AddParameter("RemotePath", sRemotePath);
ps.AddParameter("Credential", psCredential);
//ps.AddParameter("RequireIntegrity", false);
//ps.AddParameter("RequirePrivacy", false);
try
{
System.Collections.ObjectModel.Collection<PSObject> collectionResults = ps.Invoke();
foreach (PSObject psObl in collectionResults)
{
Console.WriteLine("Status : {0}", psObl.Members["Status"].Value.ToString());
Console.WriteLine("Local Path : {0}", psObl.Members["LocalPath"].Value.ToString());
Console.WriteLine("Remote Path : {0}\n", psObl.Members["RemotePath"].Value.ToString());
}
}
catch (ParameterBindingException pbe)
{
System.Console.WriteLine("\rNew-SmbGlobalMapping error : {0}: {1}",
pbe.GetType().FullName, pbe.Message);
}
}
// To get and remove the test mapping in PowerShell :
// Get-SmbGlobalMapping
// Remove-SmbGlobalMapping -RemotePath "\\DESKTOP-EOPIFM5\Windows 7" -Force

dragging and dropping from one JavaFX Application to another

I am trying to move an element from one JavaFX application to another via drag-and-drop, as far as I understand this shouldnt be a problem.
So I have an object of a class and drag it from one application to the other and then have its contents printed to the console. It's mostly looking good, I can get the drop to "accepted" or "not accepted" by playing around with transfer modes, which shows me that the mechanism itself seems to be working.
But when I drop the object on the other application a bunch of, what I believe to be mostly chinese, letters are printed to the console. This is apparently some encoding problem, but I can't really figure out what's happening, aside from the fact that both applications mainly use the same codebase, the "chinese" letters are quite numerous. The object's toString merely prints one and a half line in latin characters, but upon dropping there are several paragraphs of "chinese" letters printed.
Can anyone tell me what's happening here? Is it just a simple encoding f-up? Does the OS (Win7 btw) maybe interfer here? Have I uncovered long lost ancient chinese wisdom?
The code itself is rather simple, here is the code from the "sender"
setOnDragDetected(event ->
{
Dragboard db = startDragAndDrop(TransferMode.ANY);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.put(DataFormat.PLAIN_TEXT, treeElement.getEntities());
db.setContent(clipboardContent);
System.out.println(db.getContent(DataFormat.PLAIN_TEXT));
event.consume();
});
and here from the "receiver"
setOnDragDropped(event ->
{
Dragboard db = event.getDragboard();
if (db.hasContent(DataFormat.PLAIN_TEXT))
{
System.out.println(db.getContent(DataFormat.PLAIN_TEXT));
System.out.println("Accept Drop");
}
event.consume();
});
I just don't really see anything that would explain my error.
The issue is using DataFormat.PLAIN_TEXT. This means JavaFX considers the data format to be just what it says on the tin: text, i.e. String data. This is not really the case. There is no static member of DataFormat that refers to a suitable DataFormat, so you need to create one on your own:
final String mimeType = "application/javafx-entrylist"; // TODO: choose properly
// use existing format or introduce new one
DataFormat f = DataFormat.lookupMimeType(mimeType);
final DataFormat format = f == null ? new DataFormat(mimeType) : f;
setOnDragDetected(event -> {
Dragboard db = startDragAndDrop(TransferMode.ANY);
ClipboardContent clipboardContent = new ClipboardContent();
clipboardContent.put(format, treeElement.getEntities());
db.setContent(clipboardContent);
System.out.println(db.getContent(format));
event.consume();
});
setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
if (db.hasContent(format)) {
System.out.println(db.getContent(format));
System.out.println("Accept Drop");
}
event.consume();
});

Log4cplus setproperty function usage

I use the following configuration for my logger, in the conf file :
log4cplus.appender.TestLogAppender = log4cplus::TimeBasedRollingFileAppender
log4cplus.appender.TestLogAppender.FilenamePattern = %d{yyyyMMdd}.log
log4cplus.appender.TestLogAppender.MaxHistory = 365
log4cplus.appender.TestLogAppender.Schedule = DAILY
log4cplus.appender.TestLogAppender.RollOnClose = false
log4cplus.appender.TestLogAppender.layout = log4cplus::PatternLayout
log4cplus.appender.TestLogAppender.layout.ConversionPattern = %m%n
And in my code, I have the following initializing function for my logger, in which first, I load the configuration file, and then I wish to set the 'FilenamePattern' property to a new value, so that when I run multiple applications, each application will write to it's own log file:
void InitLogger()
{
ProperyConfigurator::doConfigure (L"LogConf.conf");
helpers:Properties prop(L"LogConf.conf");
props.setPropery(L"log4cplus.appender.TestLogAppender.FilenamePattern" ,
"Log/AppLogName.log.%d{yyyy-MM-dd}");
}
The problem is that when I run even one application, the log messages are written to the file as given in the original configuration file (in the 'FilenamePattern' property).
It seems the 'setproperty' didn't set the new value I gave it.
Is there a problem with my initializing logger function?
Have I used the setProperty method wrong?
You are obviously changing the properties after you have already configured the system, so your changes will be ignored. Do this instead:
helpers:Properties props(L"LogConf.conf");
props.setPropery(L"log4cplus.appender.TestLogAppender.FilenamePattern" ,
"Log/AppLogName.log.%d{yyyy-MM-dd}");
ProperyConfigurator propConf (props);
propConf.configure();

Void to Boolean Processing

I'm trying to check if the .save method is execute but I'm getting "Cannot convert from void to boolean" error. How could I check it? Also the img.save() doesn't work in a web applet, any clue why it doesn't work?
PImage img = get (180, 0, 620, 400);
if( img.save("img/111,jpg") )
{
fill(0, 255, 0);
}
else
{
fill(255, 0, 0);
}
Your if statement is assuming true or false will be returned by img.save() function. Therefore you need to check what the img.save() is returning. If that is a method than it cannot return any value back once it has been executed meaning you cannot compare it...
Put the method call in try/catch if you expect that the method could fail...
One thing at a time:
You set fill, but don't draw anything afterwards(at least in the code posted) *
By default some functionalities might not work without signing the applet.
You can't use PImage's save() method to do an upload for you. Here's a quote from the documentation:
It is not possible to use save() while running the program in a web
browser.
You will need to use a serverside script to pass data to which in turn will save the image to the serser. Have a look at Philho's upload sketch. Notice that he is passing data to a php script which handles writing the file on the server.
*although not as important, you can try this in the Processing IDE/locally to see the colour change:
if( img.save("img/111.jpg") )
{
fill(0, 255, 0);
}
else
{
fill(255, 0, 0);
}
rect(0,0,width,height);